From 8051c15e89398281dc0c4c199dfb49289624d94e Mon Sep 17 00:00:00 2001 From: Little Cat Date: Wed, 28 Dec 2022 19:15:38 -0400 Subject: [PATCH] spellbook: Boss battle create/manip magic words. --- toontown/battle/DistributedBattleBaseAI.py | 2 - toontown/coghq/BossbotHQBossBattle.py | 2 +- toontown/coghq/CogHQBossBattle.py | 10 ++ toontown/coghq/CogHQLoader.py | 1 + .../coghq/DistributedCashbotBossObject.py | 1 + toontown/coghq/LawbotHQBossBattle.py | 2 +- toontown/coghq/SellbotHQBossBattle.py | 2 +- toontown/distributed/PlayGame.py | 3 +- toontown/spellbook/MagicWordIndex.py | 120 ++++++++++++++++++ toontown/suit/DistributedBossCog.py | 3 + toontown/suit/DistributedBossCogAI.py | 3 + toontown/suit/DistributedBossbotBossAI.py | 28 +++- toontown/suit/DistributedCashbotBoss.py | 10 ++ toontown/suit/DistributedCashbotBossAI.py | 18 +++ toontown/suit/DistributedLawbotBossAI.py | 20 +++ toontown/suit/DistributedSellbotBossAI.py | 20 +++ 16 files changed, 238 insertions(+), 7 deletions(-) diff --git a/toontown/battle/DistributedBattleBaseAI.py b/toontown/battle/DistributedBattleBaseAI.py index 0cf6d38..ab86a50 100644 --- a/toontown/battle/DistributedBattleBaseAI.py +++ b/toontown/battle/DistributedBattleBaseAI.py @@ -714,8 +714,6 @@ class DistributedBattleBaseAI(DistributedObjectAI.DistributedObjectAI, BattleBas 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 diff --git a/toontown/coghq/BossbotHQBossBattle.py b/toontown/coghq/BossbotHQBossBattle.py index cd81c04..781eb6c 100644 --- a/toontown/coghq/BossbotHQBossBattle.py +++ b/toontown/coghq/BossbotHQBossBattle.py @@ -9,7 +9,7 @@ class BossbotHQBossBattle(CogHQBossBattle.CogHQBossBattle): def __init__(self, loader, parentFSM, doneEvent): CogHQBossBattle.CogHQBossBattle.__init__(self, loader, parentFSM, doneEvent) - self.teleportInPosHpr = (88, -214, 0, 210, 0, 0) + self.teleportInPosHpr = (-1.40, 59.78, 0, 360, 0, 0) for stateName in ['movie']: state = self.fsm.getStateNamed(stateName) state.addTransition('crane') diff --git a/toontown/coghq/CogHQBossBattle.py b/toontown/coghq/CogHQBossBattle.py index baafe44..bf0eaa1 100644 --- a/toontown/coghq/CogHQBossBattle.py +++ b/toontown/coghq/CogHQBossBattle.py @@ -110,10 +110,20 @@ class CogHQBossBattle(BattlePlace.BattlePlace): self.bossCog = bossCog if self.bossCog: self.bossCog.d_avatarEnter() + else: + # HACK: For some reason, when teleporting to a boss via a magic word, the place may load + # first instead of the boss cog. So listen to when the boss is generated. + self.acceptOnce('announceBoss', self.__bossGenerate) self._telemLimiter = TLGatherAllAvs('CogHQBossBattle', RotationLimitToH) NametagGlobals.setMasterArrowsOn(1) base.localAvatar.inventory.setRespectInvasions(0) self.fsm.request(requestStatus['how'], [requestStatus]) + + def __bossGenerate(self, boss): + if self.bossCog: + return + self.bossCog = boss + self.bossCog.d_avatarEnter() def exit(self): self.fsm.requestFinalState() diff --git a/toontown/coghq/CogHQLoader.py b/toontown/coghq/CogHQLoader.py index 419faa8..4a48b01 100644 --- a/toontown/coghq/CogHQLoader.py +++ b/toontown/coghq/CogHQLoader.py @@ -143,6 +143,7 @@ class CogHQLoader(StateData.StateData): def enterCogHQBossBattle(self, requestStatus): self.placeClass = self.getBossPlaceClass() self.enterPlace(requestStatus) + self.hood.hideTitleText() def exitCogHQBossBattle(self): self.exitPlace() diff --git a/toontown/coghq/DistributedCashbotBossObject.py b/toontown/coghq/DistributedCashbotBossObject.py index 4353b34..34cc645 100644 --- a/toontown/coghq/DistributedCashbotBossObject.py +++ b/toontown/coghq/DistributedCashbotBossObject.py @@ -1,4 +1,5 @@ from panda3d.core import * +from panda3d.physics import * from direct.interval.IntervalGlobal import * from direct.directnotify import DirectNotifyGlobal from direct.distributed import DistributedSmoothNode diff --git a/toontown/coghq/LawbotHQBossBattle.py b/toontown/coghq/LawbotHQBossBattle.py index 51805d4..2521b3d 100644 --- a/toontown/coghq/LawbotHQBossBattle.py +++ b/toontown/coghq/LawbotHQBossBattle.py @@ -9,7 +9,7 @@ class LawbotHQBossBattle(CogHQBossBattle.CogHQBossBattle): def __init__(self, loader, parentFSM, doneEvent): CogHQBossBattle.CogHQBossBattle.__init__(self, loader, parentFSM, doneEvent) - self.teleportInPosHpr = (88, -214, 0, 210, 0, 0) + self.teleportInPosHpr = (-2.84, -99.47, 0, 360, 0, 0) def load(self): CogHQBossBattle.CogHQBossBattle.load(self) diff --git a/toontown/coghq/SellbotHQBossBattle.py b/toontown/coghq/SellbotHQBossBattle.py index 411b067..a7d8826 100644 --- a/toontown/coghq/SellbotHQBossBattle.py +++ b/toontown/coghq/SellbotHQBossBattle.py @@ -9,7 +9,7 @@ class SellbotHQBossBattle(CogHQBossBattle.CogHQBossBattle): def __init__(self, loader, parentFSM, doneEvent): CogHQBossBattle.CogHQBossBattle.__init__(self, loader, parentFSM, doneEvent) - self.teleportInPosHpr = (0, 95, 18, 180, 0, 0) + self.teleportInPosHpr = (0, 61, 18, 180, 0, 0) def load(self): CogHQBossBattle.CogHQBossBattle.load(self) diff --git a/toontown/distributed/PlayGame.py b/toontown/distributed/PlayGame.py index f55ce3c..af33272 100644 --- a/toontown/distributed/PlayGame.py +++ b/toontown/distributed/PlayGame.py @@ -192,7 +192,8 @@ class PlayGame(StateData.StateData): if how in ['tunnelIn', 'teleportIn', 'doorIn', - 'elevatorIn']: + 'elevatorIn', + 'movie']: self.fsm.request('quietZone', [doneStatus]) else: self.notify.error('Exited hood with unexpected mode %s' % how) diff --git a/toontown/spellbook/MagicWordIndex.py b/toontown/spellbook/MagicWordIndex.py index d0f9cd5..3f3ceff 100644 --- a/toontown/spellbook/MagicWordIndex.py +++ b/toontown/spellbook/MagicWordIndex.py @@ -250,6 +250,9 @@ class MaxToon(MagicWord): def handleWord(self, invoker, avId, toon, *args): from toontown.toonbase import ToontownGlobals + from toontown.quest import Quests + from toontown.suit import SuitDNA + from toontown.coghq import CogDisguiseGlobals # TODO: Handle this better, like giving out all awards, set the quest tier, stuff like that. # This is mainly copied from Anesidora just so I can better work on things. @@ -271,6 +274,14 @@ class MaxToon(MagicWord): toon.b_setMoney(toon.maxMoney) toon.b_setBankMoney(toon.maxBankMoney) + toon.b_setQuests([]) + toon.b_setQuestCarryLimit(ToontownGlobals.MaxQuestCarryLimit) + toon.b_setRewardHistory(Quests.LOOPING_FINAL_TIER, []) + + toon.b_setCogParts([*CogDisguiseGlobals.PartsPerSuitBitmasks]) + toon.b_setCogTypes([SuitDNA.suitsPerDept - 1] * 4) + toon.b_setCogLevels([ToontownGlobals.MaxCogSuitLevel] * 4) + return f"Successfully maxed {toon.getName()}!" class AbortMinigame(MagicWord): @@ -339,6 +350,115 @@ class Quests(MagicWord): else: return "Valid commands: \"finish\"" +class BossBattle(MagicWord): + aliases = ["boss"] + desc = "Create a new or manupliate the current boss battle." + execLocation = MagicWordConfig.EXEC_LOC_SERVER + arguments = [("command", str, True), ("type", str, False, ""), ("start", int, False, 1)] + + def handleWord(self, invoker, avId, toon, *args): + command = args[0].lower() + type = args[1].lower() + start = args[2] + + """ + Commands: + - create [type] [start: 1]: Creates a boss and teleports to it. + - start: Starts/Restarts the battle from the beginning. + - stop: Stops the battle by going to the Frolic state. + - skip: Skips the boss to the next state (needs getNextState to be implemented). + - final: Skips the boss to the final round. + - kill: Skips the boss to the Victory state. + """ + + # create command shortcut: + if command in ("vp", "cfo", "cj", "ceo"): + type = command + command = "create" + try: + start = int(args[1]) + except ValueError: + start = 1 + + from toontown.suit.DistributedBossCogAI import DistributedBossCogAI + boss = None + for do in self.air.doId2do.values(): + if isinstance(do, DistributedBossCogAI): + if do.isToonKnown(invoker.doId): + boss = do + break + + if command == "create": + if boss: + return "You're already in a boss battle. Please finish this one." + if type == "vp": + from toontown.suit.DistributedSellbotBossAI import DistributedSellbotBossAI + boss = DistributedSellbotBossAI(self.air) + elif type == "cfo": + from toontown.suit.DistributedCashbotBossAI import DistributedCashbotBossAI + boss = DistributedCashbotBossAI(self.air) + elif type == "cj": + from toontown.suit.DistributedLawbotBossAI import DistributedLawbotBossAI + boss = DistributedLawbotBossAI(self.air) + elif type == "ceo": + from toontown.suit.DistributedBossbotBossAI import DistributedBossbotBossAI + boss = DistributedBossbotBossAI(self.air) + else: + return f"Unknown boss type: \"{type}\"" + + zoneId = self.air.allocateZone() + boss.generateWithRequired(zoneId) + if start: + boss.addToon(avId) + boss.b_setState('WaitForToons') + else: + boss.b_setState('Frolic') + + respText = f"Created {type.upper()} boss battle" + if not start: + respText += " in Frolic state" + + return respText + ", teleporting...", ["cogHQLoader", "cogHQBossBattle", "movie" if start else "teleportIn", boss.getHoodId(), boss.zoneId, 0] + + # The following commands needs the invoker to be in a boss battle. + if not boss: + return "You ain't in a boss battle! Use the \"create\" command to create a boss battle." + + boss.acceptNewToons() + if command == "start": + boss.b_setState('WaitForToons') + return "Boss battle started!" + + elif command == "stop": + boss.b_setState("Frolic") + return "Boss battle stopped!" + + elif command == "skip": + try: + nextState = boss.getNextState() + except NotImplementedError: + return "\"getNextState\" is not implemented for this boss battle!" + if nextState: + boss.b_setState(nextState) + return f"Skipped to {nextState}!" + return f"Cannot skip \"{boss.getCurrentOrNextState()}\" state." + + elif command in ("final", "pie", "crane"): + if boss.dept == 'c': + boss.b_setState("BattleFour") + else: + boss.b_setState("BattleThree") + return "Skipped to final round!" + + elif command in ("kill", "victory", "finish"): + boss.b_setState("Victory") + return "Killed the boss!" + + # The create command is already described when the invoker is not in a battle. These are the commands + # they can use INSIDE the battle. + return respText + f"Unknown command: \"{command}\". Valid commands: \"start\", \"stop\", \"skip\", \"final\", \"kill\"." + + # Instantiate all classes defined here to register them. # A bit hacky, but better than the old system for item in list(globals().values()): diff --git a/toontown/suit/DistributedBossCog.py b/toontown/suit/DistributedBossCog.py index ab17bb9..aa76277 100644 --- a/toontown/suit/DistributedBossCog.py +++ b/toontown/suit/DistributedBossCog.py @@ -118,6 +118,9 @@ class DistributedBossCog(DistributedAvatar.DistributedAvatar, BossCog.BossCog): self.bubbleF.setTag('attackCode', str(ToontownGlobals.BossCogFrontAttack)) self.bubbleF.stash() + # HACK: See toontown/coghq/CogHQBossBattle.py + messenger.send('announceBoss', [self]) + def disable(self): DistributedAvatar.DistributedAvatar.disable(self) self.battleAId = None diff --git a/toontown/suit/DistributedBossCogAI.py b/toontown/suit/DistributedBossCogAI.py index 51b995c..7dc5461 100644 --- a/toontown/suit/DistributedBossCogAI.py +++ b/toontown/suit/DistributedBossCogAI.py @@ -685,3 +685,6 @@ class DistributedBossCogAI(DistributedAvatarAI.DistributedAvatarAI): def doNextAttack(self, task): self.b_setAttackCode(ToontownGlobals.BossCogNoAttack) + + def getNextState(self): + raise NotImplementedError diff --git a/toontown/suit/DistributedBossbotBossAI.py b/toontown/suit/DistributedBossbotBossAI.py index 4bfa03d..cfabeab 100644 --- a/toontown/suit/DistributedBossbotBossAI.py +++ b/toontown/suit/DistributedBossbotBossAI.py @@ -87,7 +87,7 @@ class DistributedBossbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FS self.battleOneBattlesMade = True def getHoodId(self): - return ToontownGlobals.LawbotHQ + return ToontownGlobals.BossbotHQ def generateSuits(self, battleNumber): if battleNumber == 1: @@ -885,3 +885,29 @@ class DistributedBossbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FS def toggleMove(self): self.moveAttackAllowed = not self.moveAttackAllowed return self.moveAttackAllowed + + def getNextState(self): + currState = self.getCurrentOrNextState() + if currState == "Elevator": + return "Introduction" + elif currState == "Introduction": + return "BattleOne" + elif currState == "BattleOne": + return "PrepareBattleTwo" + elif currState == "PrepareBattleTwo": + return "BattleTwo" + elif currState == "BattleTwo": + return "PrepareBattleThree" + elif currState == "PrepareBattleThree": + return "BattleThree" + elif currState == "BattleThree": + return "PrepareBattleFour" + elif currState == "PrepareBattleFour": + return "BattleFour" + elif currState == "BattleFour": + return "Victory" + # Do not skip Victory, weird stuff may happen, like not collecting their rewards. + elif currState == "Reward": + return "Epilogue" + + return None diff --git a/toontown/suit/DistributedCashbotBoss.py b/toontown/suit/DistributedCashbotBoss.py index db5d9f7..b8186b6 100644 --- a/toontown/suit/DistributedCashbotBoss.py +++ b/toontown/suit/DistributedCashbotBoss.py @@ -19,6 +19,7 @@ from toontown.distributed import DelayDelete from toontown.chat import ResistanceChat from toontown.coghq import CogDisguiseGlobals from panda3d.core import * +from panda3d.physics import * from panda3d.otp import * import random import math @@ -481,6 +482,14 @@ class DistributedCashbotBoss(DistributedBossCog.DistributedBossCog, FSM.FSM): track.append(toon.posHprInterval(0.2, pos, hpr)) return track + + def moveLocalToonToBattleThreePos(self): + # This exists because when skipping to the crane round via a magic word, the toon gets + # teleported to the center, taking damage in the progress. This is done to remedy that. + i = self.involvedToons.index(localAvatar.doId) + if not i: + i = 0 + localAvatar.setPosHpr(*ToontownGlobals.CashbotToonsBattleThreeStartPosHpr[i]) def makeBossFleeMovie(self): hadEnough = TTLocalizer.CashbotBossHadEnough @@ -758,6 +767,7 @@ class DistributedCashbotBoss(DistributedBossCog.DistributedBossCog, FSM.FSM): self.evWalls.unstash() self.midVault.stash() self.__hideResistanceToon() + self.moveLocalToonToBattleThreePos() localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) self.generateHealthBar() self.updateHealthBar() diff --git a/toontown/suit/DistributedCashbotBossAI.py b/toontown/suit/DistributedCashbotBossAI.py index 25aaab3..1d3f6f6 100644 --- a/toontown/suit/DistributedCashbotBossAI.py +++ b/toontown/suit/DistributedCashbotBossAI.py @@ -477,3 +477,21 @@ class DistributedCashbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FS def enterEpilogue(self): DistributedBossCogAI.DistributedBossCogAI.enterEpilogue(self) self.d_setRewardId(self.rewardId) + + def getNextState(self): + currState = self.getCurrentOrNextState() + if currState == "Elevator": + return "Introduction" + elif currState == "Introduction": + return "BattleOne" + elif currState == "BattleOne": + return "PrepareBattleThree" + elif currState == "PrepareBattleThree": + return "BattleThree" + elif currState == "BattleThree": + return "Victory" + # Do not skip Victory, weird stuff may happen, like not collecting their rewards. + elif currState == "Reward": + return "Epilogue" + + return None diff --git a/toontown/suit/DistributedLawbotBossAI.py b/toontown/suit/DistributedLawbotBossAI.py index 3814324..ede2291 100644 --- a/toontown/suit/DistributedLawbotBossAI.py +++ b/toontown/suit/DistributedLawbotBossAI.py @@ -856,3 +856,23 @@ class DistributedLawbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FSM if battleDifficulty >= numDifficultyLevels: battleDifficulty = numDifficultyLevels - 1 self.b_setBattleDifficulty(battleDifficulty) + + def getNextState(self): + currState = self.getCurrentOrNextState() + if currState == "Elevator": + return "Introduction" + elif currState == "Introduction": + return "BattleOne" + elif currState == "BattleOne": + return "RollToBattleTwo" + elif currState == "RollToBattleTwo": + return "PrepareBattleTwo" + elif currState in ("PrepareBattleTwo", "BattleTwo"): + return "PrepareBattleThree" + elif currState in ("PrepareBattleThree", "BattleThree", "NearVictory"): + return "Victory" + # Do not skip Victory, weird stuff may happen, like not collecting their rewards. + elif currState == "Reward": + return "Epilogue" + + return None diff --git a/toontown/suit/DistributedSellbotBossAI.py b/toontown/suit/DistributedSellbotBossAI.py index dedfcae..9675b7e 100644 --- a/toontown/suit/DistributedSellbotBossAI.py +++ b/toontown/suit/DistributedSellbotBossAI.py @@ -452,3 +452,23 @@ class DistributedSellbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FS barrel.requestDelete() self.barrels = [] + + def getNextState(self): + currState = self.getCurrentOrNextState() + if currState == "Elevator": + return "Introduction" + elif currState == "Introduction": + return "BattleOne" + elif currState == "BattleOne": + return "RollToBattleTwo" + elif currState == "RollToBattleTwo": + return "PrepareBattleTwo" + elif currState in ("PrepareBattleTwo", "BattleTwo"): + return "PrepareBattleThree" + elif currState in ("PrepareBattleThree", "BattleThree", "NearVictory"): + return "Victory" + # Do not skip Victory, weird stuff may happen, like not collecting their rewards. + elif currState == "Reward": + return "Epilogue" + + return None