toontown-just-works/toontown/safezone/Playground.py
2024-07-07 18:08:39 -05:00

586 lines
24 KiB
Python

from direct.interval.IntervalGlobal import *
from panda3d.core 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.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
from toontown.battle import BattleParticles
from toontown.dna.DNAParser import DNABulkLoader
class Playground(Place.Place):
notify = DirectNotifyGlobal.directNotify.newCategory('Playground')
def __init__(self, loader, parentFSM, doneEvent):
Place.Place.__init__(self, loader, doneEvent)
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',
'NPCFA',
'trolley',
'final',
'doorOut',
'options',
'quest',
'purchase',
'stopped',
'fishing']),
State.State('stickerBook',
self.enterStickerBook,
self.exitStickerBook, [
'walk',
'trolley',
'final',
'doorOut',
'quest',
'purchase',
'stopped',
'fishing',
'NPCFA']),
State.State('sit',
self.enterSit,
self.exitSit, ['walk']),
State.State('drive',
self.enterDrive,
self.exitDrive, ['walk']),
State.State('trolley',
self.enterTrolley,
self.exitTrolley, [
'walk']),
State.State('doorIn',
self.enterDoorIn,
self.exitDoorIn, [
'walk',
'stopped']),
State.State('doorOut',
self.enterDoorOut,
self.exitDoorOut, [
'walk',
'stopped']),
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.eventLights = geom.findAllMatches('**/*light*')
self.loader.hood.eventLights += geom.findAllMatches('**/*lamp*')
self.loader.hood.eventLights += geom.findAllMatches('**/prop_snow_tree*')
self.loader.hood.eventLights += geom.findAllMatches('**/prop_tree*')
self.loader.hood.eventLights += geom.findAllMatches('**/*christmas*')
for light in self.loader.hood.eventLights:
light.setColorScaleOff(0)
if base.cr.newsManager.isHolidayRunning(ToontownGlobals.HALLOWEEN) 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()
elif base.cr.newsManager.isHolidayRunning(ToontownGlobals.CHRISTMAS) and self.loader.hood.snowySkyFile:
lightsOff = Sequence(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(0.7, 0.7, 0.8, 1)), Func(self.loader.hood.startSnowySky), Func(__lightDecorationOn__))
lightsOff.start()
self.snowEvent = BattleParticles.loadParticleFile('snowdisk.ptf')
self.snowEvent.setPos(0, 30, 10)
self.snowEventRender = base.cr.playGame.hood.loader.geom.attachNewNode('snowRender')
self.snowEventRender.setDepthWrite(2)
self.snowEventRender.setBin('fixed', 1)
self.snowEventFade = None
self.snowEvent.start(camera, self.snowEventRender)
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)
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.eventLights:
light.reparentTo(hidden)
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 xrange(len(points)):
p = points[i]
self.showDebugPointText(str(i), p)
def showDropPoints(self, points):
self.hideDebugPointText()
for i in xrange(len(points)):
p = points[i]
self.showDebugPointText(str(i), p)
def showPaths(self):
pass
def hidePaths(self):
self.hideDebugPointText()
def hideDebugPointText(self):
if hasattr(self, 'debugText'):
children = self.debugText.getChildren()
for i in xrange(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 doRequestLeave(self, requestStatus):
self.fsm.request('NPCFA', [requestStatus])
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):
dnaBulk = DNABulkLoader(self.loader.dnaStore, (self.safeZoneStorageDNAFile,))
dnaBulk.loadDNAFiles()
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.geom.flattenMedium()
gsg = base.win.getGsg()
if gsg:
self.geom.prepareScene(gsg)
def makeDictionaries(self, dnaStore):
self.nodeList = []
for i in xrange(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 xrange(npc.getNumPaths()):
npc.getPath(i).removeNode()