diff --git a/dependencies/astron/dclass/stride.dc b/dependencies/astron/dclass/stride.dc index 0624c127..bee3d596 100644 --- a/dependencies/astron/dclass/stride.dc +++ b/dependencies/astron/dclass/stride.dc @@ -1770,8 +1770,8 @@ dclass DistributedNPCScientist : DistributedNPCToonBase { }; dclass DistributedNPCClerk : DistributedNPCToonBase { - setMovie(uint8, uint32, uint32, int16) broadcast ram; - setInventory(blob, int16, uint8) airecv clsend; + setState(uint32, uint8) broadcast airecv clsend; + setInventory(blob, int16) airecv clsend; }; dclass DistributedNPCTailor : DistributedNPCToonBase { @@ -3174,11 +3174,11 @@ dclass TTCodeRedemptionMgr : DistributedObject { struct Friend { -uint32 doId; -string name; -blob dna; -uint16 adminAccess; -uint32 petId; + uint32 doId; + string name; + blob dna; + uint16 adminAccess; + uint32 petId; }; dclass TTSFriendsManager : DistributedObjectGlobal { diff --git a/toontown/hood/ToonHood.py b/toontown/hood/ToonHood.py index 6c43b495..a15486ec 100755 --- a/toontown/hood/ToonHood.py +++ b/toontown/hood/ToonHood.py @@ -123,7 +123,7 @@ class ToonHood(Hood): self.purchase.unload() del self.purchase - def handlePurchaseDone(self): + def handlePurchaseDone(self, playAgain): doneStatus = self.purchase.getDoneStatus() if doneStatus['where'] == 'playground': self.fsm.request('quietZone', [{'loader': 'safeZoneLoader', diff --git a/toontown/minigame/ClerkPurchase.py b/toontown/minigame/ClerkPurchase.py index 31cf7552..a6038c51 100755 --- a/toontown/minigame/ClerkPurchase.py +++ b/toontown/minigame/ClerkPurchase.py @@ -22,7 +22,6 @@ class ClerkPurchase(PurchaseBase): self.timer.reparentTo(self.frame) self.timer.posInTopRightCorner() purchaseModels.removeNode() - return def unload(self): PurchaseBase.unload(self) @@ -36,7 +35,7 @@ class ClerkPurchase(PurchaseBase): self.handleDone(0) def __timerExpired(self): - self.handleDone(0) + self.handleDone(1) def enterPurchase(self): PurchaseBase.enterPurchase(self) diff --git a/toontown/minigame/PurchaseBase.py b/toontown/minigame/PurchaseBase.py index d8b3e7a1..a5f76d8b 100755 --- a/toontown/minigame/PurchaseBase.py +++ b/toontown/minigame/PurchaseBase.py @@ -100,7 +100,7 @@ class PurchaseBase(StateData.StateData): return Task.done def handleDone(self, playAgain): - messenger.send(self.doneEvent) + messenger.send(self.doneEvent, [playAgain]) def enter(self): self.fsm.request('purchase') diff --git a/toontown/toon/DistributedNPCClerk.py b/toontown/toon/DistributedNPCClerk.py index e36c8c1d..91f2e12c 100755 --- a/toontown/toon/DistributedNPCClerk.py +++ b/toontown/toon/DistributedNPCClerk.py @@ -1,120 +1,100 @@ -from direct.interval.IntervalGlobal import Sequence -from direct.task.Task import Task -from panda3d.core import * - -from DistributedNPCToonBase import * -import NPCToons -from toontown.hood import ZoneUtil +from otp.nametag.NametagConstants import CFSpeech, CFTimeout from toontown.minigame import ClerkPurchase -from toontown.shtiker.PurchaseManagerConstants import * -from toontown.toonbase import TTLocalizer -from otp.nametag.NametagConstants import * - +from toontown.toonbase import TTLocalizer, ToontownGlobals +from toontown.toon import NPCToons +from DistributedNPCToonBase import DistributedNPCToonBase +import time class DistributedNPCClerk(DistributedNPCToonBase): + def __init__(self, cr): DistributedNPCToonBase.__init__(self, cr) - - self.purchase = None - self.isLocalToon = 0 - self.av = None - self.purchaseDoneEvent = 'purchaseDone' + self.lastCollision = 0 + self.purchaseGui = None 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) - + self.destroyDialog() DistributedNPCToonBase.disable(self) - 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 - - def resetClerk(self): + def destroyDialog(self): self.ignoreAll() + self.clearChat() 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.showNametag2d() - 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: + if self.purchaseGui: + self.purchaseGui.exit() + self.purchaseGui.unload() + self.purchaseGui = None + + def initToonState(self): + if self.name in NPCToons.ClerkPositions: + self.putOnSuit(ToontownGlobals.cogHQZoneId2deptIndex(self.zoneId), rental=True) + + self.setAnimState('neutral', 0.9, None, None) + self.updatePosition() + + def updatePosition(self): + if self.name in NPCToons.ClerkPositions: + pos = NPCToons.ClerkPositions[self.name] + self.setPos(*pos[0]) + self.setH(pos[1]) + else: + DistributedNPCToonBase.initToonState(self) + + def freeAvatar(self): + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().fsm.request('walk') + + def handleCollisionSphereEnter(self, collEntry): + if self.lastCollision > time.time(): 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: - if self.isLocalToon: - self.hideNametag2d() - 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) - seq = Sequence((camera.posQuatInterval(1, Vec3(-5, 9, self.getHeight() - 0.5), Vec3(-150, -2, 0), other=self, blendType='easeOut', name=self.uniqueName('lerpCamera')))) - seq.start() - 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.lastCollision = time.time() + ToontownGlobals.NPCCollisionDelay + + if not base.localAvatar.getMoney(): self.setChatAbsolute(TTLocalizer.STOREOWNER_NEEDJELLYBEANS, CFSpeech | CFTimeout) - self.resetClerk() - return + return + + self.d_setState(ToontownGlobals.CLERK_GREETING) + base.cr.playGame.getPlace().fsm.request('purchase') + camera.wrtReparentTo(render) + camera.posQuatInterval(1, Vec3(-5, 9, self.getHeight() - 0.5), Vec3(-150, -2, 0), other=self, blendType='easeOut', name=self.uniqueName('lerpCamera')).start() + taskMgr.doMethodLater(1.0, self.popupPurchaseGUI, self.uniqueName('popupPurchaseGUI')) + + def d_setInventory(self, inventory, money): + self.sendUpdate('setInventory', [inventory, money]) + + def d_setState(self, state): + self.sendUpdate('setState', [0, state]) + + def setState(self, avId, state): + av = base.cr.doId2do.get(avId) + if not av: + return + + if state == ToontownGlobals.CLERK_GOODBYE: + self.setChatAbsolute(TTLocalizer.STOREOWNER_GOODBYE, CFSpeech | CFTimeout) + elif state == ToontownGlobals.CLERK_GREETING: + self.lookAtAvatar(av) + self.setChatAbsolute(TTLocalizer.STOREOWNER_GREETING, CFSpeech | CFTimeout) + return + elif state == ToontownGlobals.CLERK_TOOKTOOLONG: + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech | CFTimeout) + + self.updatePosition() + 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 + self.clearChat() + self.acceptOnce('purchaseClerkDone', self.__handlePurchaseDone) + self.purchaseGui = ClerkPurchase.ClerkPurchase(base.localAvatar, NPCToons.CLERK_COUNTDOWN_TIME, 'purchaseClerkDone') + self.purchaseGui.load() + self.purchaseGui.enter() - 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 - - def d_setInventory(self, invString, money, done): - self.sendUpdate('setInventory', [invString, money, done]) + def __handlePurchaseDone(self, state): + self.d_setInventory(base.localAvatar.inventory.makeNetString(), base.localAvatar.getMoney()) + self.destroyDialog() + self.freeAvatar() + self.detectAvatars() + self.d_setState(state) \ No newline at end of file diff --git a/toontown/toon/DistributedNPCClerkAI.py b/toontown/toon/DistributedNPCClerkAI.py index be608c6d..6e24e6bc 100755 --- a/toontown/toon/DistributedNPCClerkAI.py +++ b/toontown/toon/DistributedNPCClerkAI.py @@ -1,108 +1,15 @@ -from otp.ai.AIBaseGlobal import * -from direct.task.Task import Task -from panda3d.core import * -from DistributedNPCToonBaseAI import * +from DistributedNPCToonBaseAI import DistributedNPCToonBaseAI class DistributedNPCClerkAI(DistributedNPCToonBaseAI): - def __init__(self, air, npcId): - DistributedNPCToonBaseAI.__init__(self, air, npcId) - self.timedOut = 0 + def setInventory(self, inventory, money): + av = self.air.doId2do.get(self.air.getAvatarIdFromSender()) - 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) + if not av: 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) - 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 avId in self.air.doId2do: - 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 + av.b_setMoney(money if av.inventory.validatePurchase(av.inventory.makeFromNetString(inventory), av.getMoney(), money) else av.getMoney()) + av.d_setInventory(av.inventory.makeNetString()) + + def setState(self, avId, state): + self.sendUpdate('setState', [self.air.getAvatarIdFromSender(), state]) \ No newline at end of file diff --git a/toontown/toon/DistributedNPCToonBase.py b/toontown/toon/DistributedNPCToonBase.py index 1c5c7e6d..2dfab330 100755 --- a/toontown/toon/DistributedNPCToonBase.py +++ b/toontown/toon/DistributedNPCToonBase.py @@ -112,6 +112,9 @@ class DistributedNPCToonBase(DistributedToon.DistributedToon): def setupAvatars(self, av): self.ignoreAvatars() + self.lookAtAvatar(av) + + def lookAtAvatar(self, av): av.headsUp(self, 0, 0, 0) self.headsUp(av, 0, 0, 0) av.stopLookAround() diff --git a/toontown/toon/DistributedToonAI.py b/toontown/toon/DistributedToonAI.py index 8c2f170e..4555fc76 100755 --- a/toontown/toon/DistributedToonAI.py +++ b/toontown/toon/DistributedToonAI.py @@ -4753,6 +4753,8 @@ def dna(part, value): if part == 'show': return dna.asTuple() + if part == 'showrandom': + return NPCToons.getRandomDNA(time.time(), value) return 'Invalid part: ' + part @magicWord(category=CATEGORY_ADMINISTRATOR, types=[int]) diff --git a/toontown/toon/InventoryBase.py b/toontown/toon/InventoryBase.py index c72578b3..acb81c66 100755 --- a/toontown/toon/InventoryBase.py +++ b/toontown/toon/InventoryBase.py @@ -214,7 +214,7 @@ class InventoryBase(DirectObject.DirectObject): return self.countPropsInList(newInventory) - self.totalProps def validatePurchase(self, newInventory, currentMoney, newMoney): - if newMoney > currentMoney: + if newMoney > currentMoney or newMoney < 0: self.notify.warning('Somebody lied about their money! Rejecting purchase.') return 0 newItemTotal = self.countPropsInList(newInventory) diff --git a/toontown/toon/NPCToons.py b/toontown/toon/NPCToons.py index 11a0c8b0..92f9f7e7 100755 --- a/toontown/toon/NPCToons.py +++ b/toontown/toon/NPCToons.py @@ -11576,6 +11576,12 @@ NPCToonDict = {20000: (-1, NPC_REGULAR), 10001: (10000, lnames[10001], + 'r', + 'f', + 0, + NPC_LAFF_RESTOCK), + 10002: (10000, + lnames[10002], ('fss', 'md', 'l', @@ -11593,67 +11599,43 @@ NPCToonDict = {20000: (-1, 0), 'f', 0, - NPC_LAFF_RESTOCK), + NPC_CLERK), 11001: (11000, lnames[11001], - ('dll', - 'ls', - 's', - 'm', - 6, - 0, - 6, - 6, - 9, - 3, - 9, - 3, - 2, - 4, - 0), + 'r', 'm', 0, NPC_LAFF_RESTOCK), + 11002: (11000, + lnames[11002], + 'r', + 'm', + 0, + NPC_CLERK), 12001: (12000, lnames[12001], - ('mss', - 'ls', - 's', - 'm', - 8, - 0, - 8, - 8, - 5, - 12, - 5, - 12, - 7, - 16, - 0), + 'r', 'm', 0, NPC_LAFF_RESTOCK), + 12002: (12000, + lnames[12002], + 'r', + 'm', + 0, + NPC_CLERK), 13001: (13000, lnames[13001], - ('dss', - 'ld', - 's', - 'f', - 17, - 0, - 17, - 17, - 1, - 24, - 1, - 24, - 0, - 9, - 0), + 'r', 'f', 0, - NPC_LAFF_RESTOCK)} + NPC_LAFF_RESTOCK), + 13002: (13000, + lnames[13002], + 'r', + 'f', + 0, + NPC_CLERK)} if config.GetBool('want-new-toonhall', 1): NPCToonDict[2001] = (2513, @@ -11697,9 +11679,13 @@ else: NPC_REGULAR) BlockerPositions = {TTLocalizer.Flippy: (Point3(207.4, 18.81, -0.475), 90.0)} LaffRestockPositions = {lnames[11001]: ((-27.0, -170.0, -19.6), 215.0), - lnames[12001]: ((361.9, -394.4, -23.5), 113.5), + lnames[12001]: ((361.9, -394.4, -23.5), 120.0), lnames[13001]: ((143.7, -381.4, -68.4), 0.0), lnames[10001]: ((135.0, 128.8, 0.025), -212.8)} +ClerkPositions = {lnames[11002]: ((37.3, -167.5, -19.6), 130.0), + lnames[12002]: ((361.9, -460.4, -23.5), 60.0), + lnames[13002]: ((38.3, -389.5, -68.4), 0.0), + lnames[10002]: ((84, 127.8, 0.025), -180.0)} GlovePositions = {lnames[2021]: ((101, -14, 4), -305)} del lnames zone2NpcDict = {} diff --git a/toontown/toonbase/TTLocalizerEnglish.py b/toontown/toonbase/TTLocalizerEnglish.py index 8a797d2f..a6595541 100755 --- a/toontown/toonbase/TTLocalizerEnglish.py +++ b/toontown/toonbase/TTLocalizerEnglish.py @@ -6245,9 +6245,13 @@ NPCToonNames = {20000: 'Tutorial Tom', 7010: 'Jaymo', 7011: 'Donald', 10001: 'Healer Sara', + 10002: 'Clerk Barbra', 11001: 'Healer Gabriel', + 11002: 'Clerk Brendan', 12001: 'Healer Bill', - 13001: 'Healer Clover'} + 12002: 'Clerk John', + 13001: 'Healer Clover', + 13002: 'Clerk Audrey'} zone2TitleDict = {2513: ('Toon Hall', ''), 2514: ('Toontown Bank', ''), 2516: ('Toontown School House', ''), diff --git a/toontown/toonbase/ToontownGlobals.py b/toontown/toonbase/ToontownGlobals.py index 39be7ac4..aa3563dd 100755 --- a/toontown/toonbase/ToontownGlobals.py +++ b/toontown/toonbase/ToontownGlobals.py @@ -1621,9 +1621,16 @@ BMovementSpeed = 0 BMovementSpeedMultiplier = 1.3 BugReportSite = 'https://bugs.launchpad.net/toontown-united/+filebug' +NPCCollisionDelay = 2.5 + CostPerLaffRestock = 3 + FISHSALE_COMPLETE = 0 FISHSALE_TROPHY = 1 -NPCCollisionDelay = 2.5 + +CLERK_GOODBYE = 0 +CLERK_GREETING = 1 +CLERK_TOOKTOOLONG = 2 + KnockKnockHeal = 12 KnockKnockCooldown = 600