From 82e6f68a903094e86dc0402e252ceb8f98b995a4 Mon Sep 17 00:00:00 2001 From: Toontown Super <108632596+ToontownSuperForeverMVP@users.noreply.github.com> Date: Sun, 2 Aug 2026 02:56:05 -0400 Subject: [PATCH 1/2] Graphics & Quests: Port updated OutdoorLighting pipeline and procedural tasklines with LegacyQuestDict --- GEMINI.md | 60 +- libotp/__init__.py | 13 + libotp/nametag/ChatBalloon.py | 226 + libotp/nametag/ClickablePopup.py | 102 + libotp/nametag/ColorProfile.py | 25 + libotp/nametag/MarginManager.py | 216 + libotp/nametag/MarginPopup.py | 51 + libotp/nametag/Nametag.py | 211 + libotp/nametag/Nametag2d.py | 339 + libotp/nametag/Nametag3d.py | 405 + libotp/nametag/NametagFloat2d.py | 8 + libotp/nametag/NametagFloat3d.py | 6 + libotp/nametag/NametagGlobals.py | 446 + libotp/nametag/NametagGroup.py | 427 + libotp/nametag/WhisperGlobals.py | 96 + libotp/nametag/WhisperPopup.py | 168 + libotp/nametag/__init__.py | 0 libotp/nametag/_constants.py | 13 + toontown/hood/OutdoorLighting.py | 4949 ++++++++ toontown/hood/ProceduralSky.py | 410 + toontown/quest/LegacyQuestDict.py | 15743 ++++++++++++++++++++++++ toontown/quest/QuestParser.py | 65 +- toontown/quest/Quests.py | 13 + toontown/shaders/post.vert.glsl | 16 + toontown/shaders/scene_post.frag.glsl | 192 + toontown/shaders/sky.frag.glsl | 440 + toontown/shaders/sky.vert.glsl | 32 + toontown/shaders/sunrays.frag.glsl | 65 + toontown/shaders/sunrays.vert.glsl | 14 + toontown/shaders/water.frag.glsl | 95 + toontown/shaders/water.vert.glsl | 64 + 31 files changed, 24870 insertions(+), 40 deletions(-) create mode 100644 libotp/__init__.py create mode 100644 libotp/nametag/ChatBalloon.py create mode 100644 libotp/nametag/ClickablePopup.py create mode 100644 libotp/nametag/ColorProfile.py create mode 100644 libotp/nametag/MarginManager.py create mode 100644 libotp/nametag/MarginPopup.py create mode 100644 libotp/nametag/Nametag.py create mode 100644 libotp/nametag/Nametag2d.py create mode 100644 libotp/nametag/Nametag3d.py create mode 100644 libotp/nametag/NametagFloat2d.py create mode 100644 libotp/nametag/NametagFloat3d.py create mode 100644 libotp/nametag/NametagGlobals.py create mode 100644 libotp/nametag/NametagGroup.py create mode 100644 libotp/nametag/WhisperGlobals.py create mode 100644 libotp/nametag/WhisperPopup.py create mode 100644 libotp/nametag/__init__.py create mode 100644 libotp/nametag/_constants.py create mode 100644 toontown/hood/OutdoorLighting.py create mode 100644 toontown/hood/ProceduralSky.py create mode 100644 toontown/quest/LegacyQuestDict.py create mode 100644 toontown/shaders/post.vert.glsl create mode 100644 toontown/shaders/scene_post.frag.glsl create mode 100644 toontown/shaders/sky.frag.glsl create mode 100644 toontown/shaders/sky.vert.glsl create mode 100644 toontown/shaders/sunrays.frag.glsl create mode 100644 toontown/shaders/sunrays.vert.glsl create mode 100644 toontown/shaders/water.frag.glsl create mode 100644 toontown/shaders/water.vert.glsl diff --git a/GEMINI.md b/GEMINI.md index 7679e5d..f6a02e8 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -6,9 +6,9 @@ This repository contains **Toontown Super Forever MVP** (TOONTOWN-BEST-THE-NEW-) # Codebase Architecture & Structure - **`toontown/`**: Main Toontown client logic, including game states, hoods, quest systems, shaders, GUI, and base files. - - `toontown/hood/`: Outdoor lighting and procedural sky features. + - `toontown/hood/`: Outdoor lighting (`OutdoorLighting.py`) and procedural sky (`ProceduralSky.py`) features. - `toontown/launcher/`: Local server management, launcher routines, user options. - - `toontown/quest/`: Quest automation, `AutoerManager`, `TaskAutoer`. + - `toontown/quest/`: Quest system (`Quests.py`, `LegacyQuestDict.py`, `QuestParser.py`), `AutoerManager`, `TaskAutoer`. - `toontown/safezone/`: Crane sandboxes (`DistributedTTCCraneSandbox`, AI variant). - `toontown/shaders/`: Custom visual shaders (water, sky, sunrays). - `toontown/shtiker/`: Custom Shtiker Book pages (`AutoerPage`). @@ -19,21 +19,41 @@ This repository contains **Toontown Super Forever MVP** (TOONTOWN-BEST-THE-NEW-) - **`tools/`**: Development, maintenance, and asset management scripts. - **`win32/`, `linux/`, `darwin/`**: Engine launcher scripts and platform-specific binaries/tools. -# Git Status Analysis -- **Current Working Branch**: `feature/modern-loading-system` -- **Tracked & Staged Features**: - - Full Modern Loading System (`ModernLoadingScreen.py`, `LoadingAssetPreview.py`, `ZonePrefetchCatalog.py`) - - Integration with `ToontownLoader.py`, `ToontownLoadingScreen.py`, and `AssetCache.py` +# Porting Plan: OutdoorLighting System & Procedural Taskline/Quests -# Open Pull Requests -- **PR #9**: `UI & Performance: Full modern loading system with 3D asset preview and async prefetching` (`feature/modern-loading-system`) -- **PR #7**: `Core: Codebase fixes, Astron compatibility, and widescreen offsets` (`feature/core-codebase-fixes`) -- **PR #6**: `Automation: Add bot scripts, quest autoers, and custom shtiker page` (`feature/automation-bots`) -- **PR #5**: `Graphics: Custom shaders (water, sky, sunrays) and outdoor lighting` (`feature/graphics-shaders`) -- **PR #4**: `Performance: Add animation interpolation, async prefetch, and event flood prevention` (`feature/performance-optimizations`) -- **PR #3**: `GUI: Add Modern Loading Screen and Asset Preview Overlay` (`feature/modern-loading-screen`) -- **PR #2**: `Launcher: Improve Windows startup scripts and add LocalServerManager` (`feature/launcher-and-servers`) -- **PR #1**: `Update from task f885637e-e2a0-4db6-b2cf-e8297682242a` (`optimizing-guis-for-widescreen-2242a`) +Source: `C:\Users\Shadow\Desktop\scfo\supercfo` +Target: `C:\Users\Shadow\Desktop\ttbtn` + +## Phase 1: Fully Updated OutdoorLighting System Port +- **Source Modules**: + - `C:\Users\Shadow\Desktop\scfo\supercfo\OutdoorLighting.py` (212 KB full engine) + - `C:\Users\Shadow\Desktop\scfo\supercfo\ProceduralSky.py` (19.7 KB procedural sky) + - `C:\Users\Shadow\Desktop\scfo\supercfo\shaders\` (`sky.vert/frag.glsl`, `sunrays.vert/frag.glsl`, `water.vert/frag.glsl`) +- **Key Enhancements to Integrate into `toontown/hood/OutdoorLighting.py` & `toontown/hood/ProceduralSky.py`**: + - Multithreaded Cull/Draw render-state safety (`_supportsBasicShaders()` GSG query & thread guards). + - Dynamic shadow map depth-pass preservation (`ColorWriteAttrib` composition via `addAttrib()`). + - `DepthOffsetAttrib` non-destructive application preventing Z-fighting on static geometry. + - Soft light rig destruction preserving `_prevShadowCasterState` to eliminate day/night cycle rebuild blinks. + - In-place video settings updates without freezing or window property deadlocks. + - Sync GLSL shader assets to `toontown/shaders/`. + +## Phase 2: Procedural Taskline & Quests System Port +- **Source Modules**: + - `C:\Users\Shadow\Desktop\scfo\supercfo\toontown\quest\LegacyQuestDict.py` (15,743 lines of procedural quest chains) + - `C:\Users\Shadow\Desktop\scfo\supercfo\toontown\quest\Quests.py` (Updated quest management & reward dict generators) + - `C:\Users\Shadow\Desktop\scfo\supercfo\toontown\quest\QuestParser.py` (Dialogue script tokenizer/parser) +- **Key Enhancements to Integrate into `toontown/quest/`**: + - Port `LegacyQuestDict.py` into `toontown/quest/LegacyQuestDict.py`. + - Add `WANT_LEGACY_QUESTS` flag & dictionary merger in `toontown/quest/Quests.py`. + - Update quest tier constants (`TT_TIER`, `DD_TIER`, `DG_TIER`, `MM_TIER`, `BR_TIER`, `DL_TIER`, `LAWBOT_HQ_TIER`, `BOSSBOT_HQ_TIER`, `ELDER_TIER`). + - Add `questExists` compatibility helper for AI call sites. + - Integrate `QuestParser.py` for tokenized dialogue scripts. + - Ensure compatibility with `TaskAutoer.py` and `AutoerManager.py` quest automation engines. + +## Phase 3: Integration & Verification +- Test client startup with `OutdoorLighting` and `ProceduralSky` enabled. +- Verify zone switching (TTC, DD, DG, MM, BR, DL, Cog HQs) under multi-threaded rendering mode. +- Verify quest assignment and completion in AI server repository (`QuestManagerAI.py`). # Completed Work - [x] Implemented `ModernLoadingScreen.py`: Full-screen launcher/game load screen with animated gradient background, pulsing lighting, progress bar, asset captions, tip dict support, and smooth outro fade sequence. @@ -44,6 +64,10 @@ This repository contains **Toontown Super Forever MVP** (TOONTOWN-BEST-THE-NEW-) - [x] Submitted Pull Request #9 to GitHub repository (`feature/modern-loading-system`). # TODO List for Future AI Instances -- [ ] Merge and review PR #9 into `develop`. +- [ ] Port updated `OutdoorLighting.py` (212 KB engine) and `ProceduralSky.py` into `toontown/hood/`. +- [ ] Port GLSL shaders (`sky`, `sunrays`, `water`) into `toontown/shaders/`. +- [ ] Port `LegacyQuestDict.py` and update `toontown/quest/Quests.py` with procedural quest chains. +- [ ] Port `QuestParser.py` into `toontown/quest/`. +- [ ] Verify `TaskAutoer.py` quest automation compatibility with the procedural quest dict. - [ ] Validate server stability with Astron local launcher under heavy zone switching. -- [ ] Test full client load times across low-end and high-end configurations. + diff --git a/libotp/__init__.py b/libotp/__init__.py new file mode 100644 index 0000000..00e1b59 --- /dev/null +++ b/libotp/__init__.py @@ -0,0 +1,13 @@ +from .nametag import NametagGlobals +from .nametag.ChatBalloon import ChatBalloon +from .nametag.ClickablePopup import ClickablePopup +from .nametag.MarginManager import MarginManager +from .nametag.MarginPopup import MarginPopup +from .nametag.Nametag import Nametag +from .nametag.Nametag2d import Nametag2d +from .nametag.Nametag3d import Nametag3d +from .nametag.NametagFloat2d import NametagFloat2d +from .nametag.NametagFloat3d import NametagFloat3d +from .nametag.NametagGroup import NametagGroup +from .nametag.WhisperPopup import WhisperPopup +from .nametag._constants import * diff --git a/libotp/nametag/ChatBalloon.py b/libotp/nametag/ChatBalloon.py new file mode 100644 index 0000000..b41bc13 --- /dev/null +++ b/libotp/nametag/ChatBalloon.py @@ -0,0 +1,226 @@ +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * + +from . import NametagGlobals + + +class ChatBalloon: + notify = DirectNotifyGlobal.directNotify.newCategory('ChatBalloon') + + def __init__(self, node=None): + self.m_copy_node = None + self.m_top_node = None + self.m_top_mat = None + self.m_middle_node = None + self.m_middle_mat = None + self.m_bottom_node = None + self.m_bottom_mat = None + + self.m_hscale = 0 + self.m_text_height = 0 + self.m_text_frame = Vec4(0) + + self.scan(node) + + @staticmethod + def find_geom_node(node): + if node.isGeomNode(): + return node + + for i in range(node.getNumChildren()): + n = ChatBalloon.find_geom_node(node.getChild(i)) + if n: + return n + + return None + + @staticmethod + def find_middle_geom(node): + if not node.getNumChildren(): + return None + + child = None + for i in range(node.getNumChildren()): + child = node.getChild(i) + if child.getName() == 'middle': + return n + + n = ChatBalloon.find_middle_geom(child) + if n: + return n + + return ChatBalloon.find_geom_node(child) + + def scan(self, node): + if node.getName() == 'chatBalloon': + return self.scan_balloon(node) + + for i in range(node.getNumChildren()): + if self.scan(node.getChild(i)): + return True + + return False + + def scan_balloon(self, node): + self.m_copy_node = node.copySubgraph() + + for i in range(node.getNumChildren()): + child = node.getChild(i) + if child.getName() == 'top': + self.m_top_node = child + self.m_top_mat = child.getTransform().getMat() + + elif child.getName() == 'middle': + self.m_middle_node = child + self.m_middle_mat = child.getTransform().getMat() + + elif child.getName() == 'bottom': + self.m_bottom_node = child + self.m_bottom_mat = child.getTransform().getMat() + + if self.m_top_node and self.m_middle_node and self.m_bottom_node: + return True + + else: + self.notify.warning('ChatBalloon geometry does not include top, middle, and bottom nodes.') + return False + + def generate(self, text, font, wordwrap, text_color, balloon_color, for_3d, + has_draw_order, draw_order, page_button, space_for_button, + reversed, new_button): # new_button is a pointer, let's use a list hack here + chat_node = PandaNode('chat') + chat_node.setAttrib(CullFaceAttrib.make(0)) + text_node = NametagGlobals.getTextNode() + text_node.setFont(font) + text_node.setWordwrap(wordwrap) + text_node.setAlign(TextNode.ALeft) + text_node.setText(text) + + v116 = NametagGlobals._balloon_text_origin[0] + if reversed: + v116 = v116 + 9.0 + + v27 = (text_node.getRight() - text_node.getLeft()) * 0.11111111 + self.m_hscale = v27 + + if v27 < 0.25: + self.m_hscale = 0.25 + text_node.setAlign(TextNode.ACenter) + + v29 = v116 + if not reversed: + v116 = v29 + 4.5 + + else: + v116 = v29 - 4.5 + + elif reversed: + self.m_hscale = -self.m_hscale + + self.m_text_frame = text_node.getCardActual() + _space = 0.2 if space_for_button else 0.0 + num_rows = max(1, text_node.getNumRows()) + _num_rows = num_rows + line_h = text_node.getFont().getLineHeight() + + num_rows_minus_1 = num_rows - 1 + subgraph_copy_mat = Mat4(self.m_hscale, 0, 0, 0, + 0, 1.0, 0, 0, + 0, 0, 1.0, 0, + 0, 0, 0, 1.0) + text_h = _num_rows * line_h + _space + self.m_text_height = text_h + + v132 = num_rows_minus_1 * line_h + _space + + middle_mat = Mat4(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, text_h, 0, + 0, 0, 0, 1) * self.m_middle_mat + top_mat = Mat4(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, text_h - 1.0, 1) * self.m_top_mat + + v137 = v116 * self.m_hscale + v138 = 0.0 + v139 = NametagGlobals._balloon_text_origin[2] + v132 + 0.2 + + self.m_text_frame += Vec4(v137, v137, v139, v139) + + ''' + Correct code: + Python won't let us edit this transform, we'll have to copy it all + + if self.m_top_node: + self.m_top_node.setTransform(TransformState.makeMat(top_mat)) + + if self.m_middle_node: + self.m_middle_node.setTransform(TransformState.makeMat(middle_mat)) + + subgraph_copy = self.m_copy_node.copySubgraph() + chat_node.addChild(subgraph_copy) + subgraph_copy.setTransform(TransformState.makeMat(subgraph_copy_mat)) + ''' + + # BEGIN PYTHON CODE + subgraph_copy = self.m_copy_node.copySubgraph() + NodePath.anyPath(subgraph_copy).find('**/top').node().setTransform(TransformState.makeMat(top_mat)) + NodePath.anyPath(subgraph_copy).find('**/middle').node().setTransform(TransformState.makeMat(middle_mat)) + + chat_node.addChild(subgraph_copy) + subgraph_copy.setTransform(TransformState.makeMat(subgraph_copy_mat)) + # END PYTHON CODE + + if has_draw_order: + bin = config.GetString('nametag-fixed-bin', 'fixed') + subgraph_copy.setAttrib(CullBinAttrib.make(bin, draw_order)) + + subgraph_copy.setAttrib(ColorAttrib.makeFlat(balloon_color)) + if balloon_color[3] != 1.0: + subgraph_copy.setAttrib(TransparencyAttrib.make(1)) + + reducer = SceneGraphReducer() + reducer.applyAttribs(subgraph_copy) + reducer.flatten(chat_node, -1) + + generated_text = text_node.generate() + if for_3d: + v86 = ChatBalloon.find_middle_geom(chat_node) + if not v86: + v86 = ChatBalloon.find_geom_node(chat_node) + + v86.setEffect(DecalEffect.make()) + + else: + v86 = chat_node + if has_draw_order: + bin = config.GetString('nametag-fixed-bin', 'fixed') + generated_text.setAttrib(CullBinAttrib.make(bin, draw_order + 1)) + + np = NodePath.anyPath(v86) + v144 = np.attachNewNode(generated_text) + v144.setPos((v137, v138, v139)) + v144.setColor(text_color) + v144.setY(-0.01) # Panda3D 1.10 hack to prevent z-fighting. + if text_color[3] != 1.0: + v144.setTransparency(1) + + if page_button: + v116 = ModelNode('button') + new_button[0] = np.attachNewNode(v116) + button_copy = page_button.copyTo(new_button[0]) + if reversed: + button_copy.setPos(self.m_hscale * 1.7, 0, 1.8) + + else: + button_copy.setPos(self.m_hscale * 9.0, 0, 1.8) + + button_copy.setScale(8.0, 8.0, 8.0) + button_copy.setY(-0.01) # Panda3D 1.10 hack to prevent z-fighting. + + reducer = SceneGraphReducer() + reducer.applyAttribs(generated_text) + reducer.flatten(chat_node, 1) + + return chat_node diff --git a/libotp/nametag/ClickablePopup.py b/libotp/nametag/ClickablePopup.py new file mode 100644 index 0000000..49679b1 --- /dev/null +++ b/libotp/nametag/ClickablePopup.py @@ -0,0 +1,102 @@ +from direct.showbase.DirectObject import DirectObject +from panda3d.core import * + +from . import NametagGlobals +from direct.gui.DirectGuiGlobals import PGButton + + +class PopupMouseWatcherRegion(MouseWatcherRegion): + """ + This is an ultra hacky class! + The correct implementation of PopupMouseWatcherRegion cannot be done in Python. + This also assumes that m_mouse_watcher is NametagGlobals::_mouse_watcher. + """ + + class _Param: + def __init__(self, outside=False): + self.outside = outside + + def isOutside(self): + return self.outside + + def getButton(self): + return MouseButton.one() + + MOUSE_WATCHER_SETUP = False + + def __init__(self, obj, name, frame): + MouseWatcherRegion.__init__(self, '%s-%s' % (name, id(self)), frame) + + self.obj = obj + self.__inside = False + + if not self.MOUSE_WATCHER_SETUP: + NametagGlobals._mouse_watcher.setEnterPattern('mouse-enter-%r') + NametagGlobals._mouse_watcher.setLeavePattern('mouse-leave-%r') + NametagGlobals._mouse_watcher.setButtonDownPattern('button-down-%r') + NametagGlobals._mouse_watcher.setButtonUpPattern('button-up-%r') + self.MOUSE_WATCHER_SETUP = True + + self.slaveObject = DirectObject() + self.slaveObject.accept(self.__getEvent(NametagGlobals._mouse_watcher.getEnterPattern()), self.__mouseEnter) + self.slaveObject.accept(self.__getEvent(NametagGlobals._mouse_watcher.getLeavePattern()), self.__mouseLeave) + self.slaveObject.accept(self.__getEvent(NametagGlobals._mouse_watcher.getButtonDownPattern()), + self.__buttonDown) + self.slaveObject.accept(self.__getEvent(NametagGlobals._mouse_watcher.getButtonUpPattern()), self.__buttonUp) + + def __mouseEnter(self, region, extra): + self.__inside = True + self.obj.enterRegion(None) + + def __mouseLeave(self, region, extra): + self.__inside = False + self.obj.exitRegion(None) + + def __buttonDown(self, region, button): + if button == 'mouse1': + self.obj.press(PopupMouseWatcherRegion._Param()) + + def __buttonUp(self, region, button): + if button == 'mouse1': + self.obj.release(PopupMouseWatcherRegion._Param(not self.__inside)) + + def __getEvent(self, pattern): + return pattern.replace('%r', self.getName()) + + +class ClickablePopup: + def __init__(self): + self.m_state = PGButton.SReady + + def setState(self, state): + if state != self.m_state: + self.m_state = state + self.updateContents() + + def enterRegion(self, arg): + if NametagGlobals._rollover_sound: + NametagGlobals._rollover_sound.play() + + self.setState(PGButton.SRollover) + + def exitRegion(self, arg): + self.setState(PGButton.SReady) + + def press(self, arg): + if arg.getButton() == MouseButton.one(): + if NametagGlobals._click_sound: + NametagGlobals._click_sound.play() + self.setState(PGButton.SDepressed) + + def release(self, arg): + if arg.getButton() == MouseButton.one(): + if arg.isOutside(): + self.setState(PGButton.SReady) + + else: + self.setState(PGButton.SRollover) + self.click() + + def _createRegion(self, frame): + name = '%s-%s' % (self.__class__.__name__, self.getName()) + return PopupMouseWatcherRegion(self, name, frame) diff --git a/libotp/nametag/ColorProfile.py b/libotp/nametag/ColorProfile.py new file mode 100644 index 0000000..84a1ec7 --- /dev/null +++ b/libotp/nametag/ColorProfile.py @@ -0,0 +1,25 @@ +from panda3d.core import Vec4 +from direct.gui.DirectGuiGlobals import PGButton + +class ColorProfile: + def __init__(self, clickable=Vec4(0, 0, 0, 1), hover=Vec4(0, 0, 0, 1), pressed=Vec4(0, 0, 0, 1), disabled=Vec4(0, 0, 0, 1)): + self.clickable = Vec4(clickable) + self.hover = Vec4(hover) + self.pressed = Vec4(pressed) + self.disabled = Vec4(disabled) + + def copy(self): + return ColorProfile(self.clickable, self.hover, self.pressed, self.disabled) + + def getColorFromState(self, state): + if state == PGButton.SReady: + return Vec4(self.clickable) + elif state == PGButton.SDepressed: + return Vec4(self.pressed) + elif state == PGButton.SRollover: + return Vec4(self.hover) + elif state == PGButton.SInactive: + return Vec4(self.disabled) + return Vec4(self.clickable) + +GRAY = ColorProfile(Vec4(0.5, 0.5, 0.5, 1), Vec4(0.6, 0.6, 0.6, 1), Vec4(0.4, 0.4, 0.4, 1), Vec4(0.3, 0.3, 0.3, 1)) \ No newline at end of file diff --git a/libotp/nametag/MarginManager.py b/libotp/nametag/MarginManager.py new file mode 100644 index 0000000..adf53bf --- /dev/null +++ b/libotp/nametag/MarginManager.py @@ -0,0 +1,216 @@ +from panda3d.core import * + + +class PopupHandle: + def __init__(self, popup): + self.m_popup = popup # 12 + self.m_cell = -1 # 16 + self.m_wants_visible = False # 20 + self.m_score = 0 # 24 + self.m_objcode = id(self) # 28 + popup.setObjectCode(self.m_objcode) + + +class MarginCell: + def __init__(self): + self.m_mat = Mat4() # 0 + self.m_cell_width = 0 # 64 + self.m_popup = None # 68 + self.m_np = None # 72 + self.m_visible = False # 84 + self.m_objcode = 0 # 88 + self.m_time = 0 # 96 + + +class MarginManager(PandaNode): + def __init__(self): + PandaNode.__init__(self, 'popups') + + # self.cbNode = CallbackNode(self.getName() + '-cbNode') + # self.cbNode.setCullCallback(PythonCallbackObject(self.cullCallback)) + # self.addChild(self.cbNode) + + self.m_cells = [] + self.m_popups = {} # MarginPopup*: PopupHandle + self.m_code_map = {} # code: MarginPopup* + self.m_num_available = 0 + + def addGridCell(self, a2, a3, a4, a5, a6, a7, newParent, newPos): + v7 = (a5 - a4) * 0.16666667 + v8 = (a7 - a6) * 0.16666667 + v15 = v7 * a2 + a4 + v9 = a3 * v8 + a6 + v10 = v9 + v8 - 0.01 + v11 = v9 + 0.01 + v12 = v15 + v7 - 0.01 + v13 = v15 + 0.01 + return self.addCell(v13, v12, v11, v10, newParent, newPos) + + def addCell(self, left, right, bottom, top, newParent, newPos): + v5 = (top - bottom) * 0.5 + v19 = Vec3(v5, 0, 0) + scale = Vec3(v5, v5, v5) + shear = Vec3(0, 0, 0) + trans = Vec3((left + right) * 0.5, 0, (bottom + top) * 0.5) + + v18 = len(self.m_cells) + v9 = MarginCell() + self.m_cells.append((v9, newParent, newPos)) + v9.m_available = True + + mat3 = Mat3() + composeMatrix(mat3, scale, shear, Vec3(0, 0, 0), 0) + v9.m_mat = Mat4(mat3, trans) + + v9.m_cell_width = (right - left) * 0.5 / v19[0] + v9.m_np = None + v9.m_popup = None + v9.m_objcode = 0 + v9.m_time = 0.0 + + self.m_num_available += 1 + return v18 + + def setCellAvailable(self, a2, a3): + v5 = self.m_cells[a2][0] + if v5.m_available: + self.m_num_available -= 1 + + v5.m_available = a3 + if v5.m_available: + self.m_num_available += 1 + + if v5.m_np: + self.hide(a2) + v5.m_popup = None + v5.m_objcode = 0 + + def getCellAvailable(self, a2): + return self.m_cells[a2][0].m_available + + def cullCallback(self, *args): + # Under Cull/Draw threading models, executing update() inside the Cull thread + # causes concurrent scene graph modifications (attachNewNode, reparentTo) which + # result in fatal C++ assertions (e.g. reparented at nodePath.cxx). + # We rely entirely on the App-thread task in ToonBase.py to update margins. + pass + + def managePopup(self, a2): + a2.setManaged(True) + self.m_popups[a2] = PopupHandle(a2) + self.m_code_map[a2.getObjectCode()] = a2 + + def unmanagePopup(self, a2): + v9 = self.m_popups.get(a2) + if v9: + if v9.m_cell >= 0: + self.hide(v9.m_cell) + v9.m_cell = -1 + + a2.setManaged(False) + del self.m_popups[a2] + del self.m_code_map[v9.m_objcode] + + def hide(self, a2): + cell = self.m_cells[a2][0] + cell.m_np.removeNode() + cell.m_time = globalClock.getFrameTime() + if cell.m_popup: + cell.m_popup.setVisible(False) + + def show(self, popup, cell_index): + v12 = self.m_cells[cell_index][0] + v12.m_popup = popup + v12.m_objcode = popup.getObjectCode() + v12.m_np = NodePath.anyPath(self).attachNewNode(popup) + v12.m_np.setMat(v12.m_mat) + v12.m_np.reparentTo(self.m_cells[cell_index][1]) + v12.m_np.setPos(self.m_cells[cell_index][2]) + self.m_popups[popup].m_cell = cell_index + popup.m_cell_width = v12.m_cell_width + popup.setVisible(True) + + def chooseCell(self, a2, a3): + now = globalClock.getFrameTime() + objcode = a2.getObjectCode() + + for cell in a3: + v7 = self.m_cells[cell][0] + if (v7.m_popup == a2 or v7.m_objcode == objcode) and (now - v7.m_time) <= 30.0: + result = cell + break + + else: + for cell in a3[::-1][1:]: # Iterate backwards, skip last item + v10 = self.m_cells[cell][0] + if (not v10.m_popup) or (now - v10.m_time) > 30.0: + result = cell + break + + else: + result = a3[-1] + + a3.remove(result) + return result + + def showVisibleNoConflict(self): + cells = [] + for i, cell in enumerate(self.m_cells): + if cell[0].m_available and not cell[0].m_np: + cells.append(i) + + for handle in self.m_popups.values(): + v7 = handle.m_popup + if handle.m_wants_visible and not v7.isVisible(): + v8 = self.chooseCell(v7, cells) + self.show(v7, v8) + + def showVisibleResolveConflict(self): + v4 = [] + + for handle in self.m_popups.values(): + score = 0 + if handle.m_wants_visible: + score = handle.m_score + + v4.append((handle, -score)) + + v4 = sorted(v4, key=lambda a: a[-1]) + for handle in v4[self.m_num_available:]: + if handle[0].m_popup.isVisible(): + self.hide(handle[0].m_cell) + handle[0].m_cell = -1 + + cells = [] + for i, cell in enumerate(self.m_cells): + if cell[0].m_available and not cell[0].m_np: + cells.append(i) + + for handle in v4[:self.m_num_available]: + v7 = handle[0].m_popup + if handle[0].m_wants_visible and not v7.isVisible(): + v8 = self.chooseCell(v7, cells) + self.show(v7, v8) + + def update(self): + num_want_visible = 0 + + for handle in list(self.m_popups.values()): + popup = handle.m_popup + handle.m_wants_visible = popup.considerVisible() + if handle.m_wants_visible and handle.m_objcode: + handle.m_score = popup.getScore() + num_want_visible += 1 + + elif popup.isVisible(): + self.hide(handle.m_cell) + handle.m_cell = -1 + + if num_want_visible > self.m_num_available: + self.showVisibleResolveConflict() + + else: + self.showVisibleNoConflict() + + for popup in self.m_popups.keys(): + popup.frameCallback() diff --git a/libotp/nametag/MarginPopup.py b/libotp/nametag/MarginPopup.py new file mode 100644 index 0000000..f9b6439 --- /dev/null +++ b/libotp/nametag/MarginPopup.py @@ -0,0 +1,51 @@ +from panda3d.core import * + +from . import NametagGlobals + + +class MarginPopup(PandaNode): + def __init__(self): + PandaNode.__init__(self, 'MarginPopup') + + self.m_managed = False + self.m_visible = False + self.m_np = None + self.m_cell_width = 1.0 + self.m_seq = NametagGlobals._margin_prop_seq + + def getCellWidth(self): + return self.m_cell_width + + def setManaged(self, value): + self.m_managed = value + if value: + self.m_np = NodePath.anyPath(self) + + else: + self.m_np = None + + def isManaged(self): + return self.m_managed + + def setVisible(self, value): + self.m_visible = value + + def isVisible(self): + return self.m_visible + + def getScore(self): + return 0.0 + + def getObjectCode(self): + return 0 + + def considerVisible(self): + if self.m_seq != NametagGlobals._margin_prop_seq: + self.m_seq = NametagGlobals._margin_prop_seq + self.updateContents() + + def updateContents(self): + pass + + def frameCallback(self): + pass diff --git a/libotp/nametag/Nametag.py b/libotp/nametag/Nametag.py new file mode 100644 index 0000000..95e7275 --- /dev/null +++ b/libotp/nametag/Nametag.py @@ -0,0 +1,211 @@ +from .ClickablePopup import * +from ._constants import * +from .ColorProfile import ColorProfile, GRAY + + +class Nametag(ClickablePopup): + CName = 1 + CSpeech = 2 + CThought = 4 + + def __init__(self, wordwrap): + ClickablePopup.__init__(self) + + self.m_avatar = None + self.m_ival = None + self.m_popup_region = None + self.m_seq = 0 + self.m_mouse_watcher = None + self.m_draw_order = 0 + self.m_has_draw_order = False + + self.m_contents = CFSpeech | CFThought | CFQuicktalker + self.m_active = True + self.m_field_12 = 0 + self.m_group = None + self.m_wordwrap = wordwrap + self.m_has_region = False + self.m_ival_name = 'flash-%d' % id(self) + self._colorProfile: ColorProfile = GRAY + self._useColorProfile: bool = False + + # Optional AP-teams style color profile support. + # Some forks call these methods from Nametag2d; provide safe defaults. + def usingColorProfile(self) -> bool: + return bool(getattr(self, '_useColorProfile', False)) + + def setUsingColorProfile(self, use: bool): + self._useColorProfile = bool(use) + self.updateContents() + + def setColorProfile(self, profile: ColorProfile): + self._colorProfile = profile if profile is not None else GRAY + self._useColorProfile = True + self.updateContents() + + def getColorProfile(self) -> ColorProfile: + return getattr(self, '_colorProfile', GRAY) + + def clearAvatar(self): + self.m_avatar = None + + def clearDrawOrder(self): + self.m_has_draw_order = False + self.updateContents() + + def click(self): + if self.m_group: + self.m_group.click() + + def deactivate(self): + if self.m_has_region: + if self.m_mouse_watcher: + self.m_mouse_watcher.removeRegion(self.m_popup_region) + self.m_mouse_watcher = None + + self.m_has_region = None + + self.m_seq = 0 + + def determineContents(self): + if self.m_group and self.m_group.isManaged(): + v3 = self.m_contents & self.m_group.getContents() + v4 = self.m_group.m_chat_flags + + if v4 & CFSpeech: + if v3 & Nametag.CSpeech: + return Nametag.CSpeech + + elif v4 & CFThought and v3 & Nametag.CThought: + return Nametag.CThought + + if v3 & Nametag.CName and self.m_group.getName() and NametagGlobals._master_nametags_visible: + return Nametag.CName + + return 0 + + def displayAsActive(self): + if not self.m_active: + return 0 + + if self.m_group: + return self.m_group.displayAsActive() + + else: + return NametagGlobals._master_nametags_active + + def setAvatar(self, avatar): + self.m_avatar = avatar + + def getAvatar(self): + return self.m_avatar + + def setChatWordwrap(self, wordwrap): + self.m_wordwrap = wordwrap + + def getChatWordwrap(self): + return self.m_wordwrap + + def getGroup(self): + return self.m_group + + def getState(self): + if self.m_group: + if not (self.m_active and self.m_group.displayAsActive()): + return PGButton.SInactive + + elif not (self.m_active and NametagGlobals._master_nametags_active): + return PGButton.SInactive + + return self.m_state + + def hasGroup(self): + return self.m_group is not None + + def setActive(self, active): + self.m_active = active + self.updateContents() + + def isActive(self): + return self.m_active + + def isGroupManaged(self): + return self.m_group and self.m_group.isManaged() + + def keepRegion(self): + if self.m_popup_region: + self.m_seq = self.m_group.getRegionSeq() + + def manage(self, manager): + self.updateContents() + + def unmanage(self, manager): + self.updateContents() + self.deactivate() + + def setContents(self, contents): + self.m_contents = contents + self.updateContents() + + def setDrawOrder(self, draw_order): + self.m_draw_order = draw_order + self.m_has_draw_order = True + self.updateContents() + + def setRegion(self, frame, sort): + if self.m_popup_region: + self.m_popup_region.setFrame(frame) + + else: + self.m_popup_region = self._createRegion(frame) + + self.m_popup_region.setSort(int(sort)) + self.m_seq = self.m_group.getRegionSeq() + + def startFlash(self, np): + self.stopFlash() + self.m_ival = Sequence( + np.colorInterval(0.5, Vec4(1.0, 1.0, 1.0, 0.5), startColor=Vec4(1.0, 1.0, 1.0, 1.0), blendType='easeOut'), + np.colorInterval(0.5, Vec4(1.0, 1.0, 1.0, 1.0), startColor=Vec4(1.0, 1.0, 1.0, 0.5), blendType='easeIn')) + self.m_ival.loop() + + def stopFlash(self): + if self.m_ival: + self.m_ival.finish() + self.m_ival = None + + def updateRegion(self, seq): + if seq == self.m_seq: + is_active = self.displayAsActive() + + else: + is_active = False + + if self.m_has_region: + if self.m_mouse_watcher != NametagGlobals._mouse_watcher: + if self.m_mouse_watcher: + self.m_mouse_watcher.removeRegion(self.m_popup_region) + + self.m_has_region = False + self.setState(PGButton.SReady) + + if is_active: + if (not self.m_has_region) and self.m_popup_region: + if self.m_mouse_watcher != NametagGlobals._mouse_watcher: + self.m_mouse_watcher = NametagGlobals._mouse_watcher + + if self.m_mouse_watcher: + self.m_mouse_watcher.addRegion(self.m_popup_region) + + self.m_has_region = True + + elif self.m_has_region: + if self.m_mouse_watcher and self.m_popup_region: + self.m_mouse_watcher.removeRegion(self.m_popup_region) + + self.m_has_region = False + self.m_mouse_watcher = None + self.setState(PGButton.SReady) + + def upcastToPandaNode(self): + return self diff --git a/libotp/nametag/Nametag2d.py b/libotp/nametag/Nametag2d.py new file mode 100644 index 0000000..bd23775 --- /dev/null +++ b/libotp/nametag/Nametag2d.py @@ -0,0 +1,339 @@ +import math + +from panda3d.core import * + +from . import NametagGlobals +from .MarginPopup import MarginPopup +from .Nametag import Nametag +from ._constants import * + + +class Nametag2d(Nametag, MarginPopup): + def __init__(self): + Nametag.__init__(self, 8.075) + MarginPopup.__init__(self) + + self.m_copied_np = None + self.m_attached_np = None + self.m_arrow = None + self.m_unknown_np = None + + # self.cbNode = CallbackNode(self.getName() + '-cbNode') + # self.cbNode.setCullCallback(PythonCallbackObject(self.cullCallback)) + # self.addChild(self.cbNode) + NametagGlobals._active_nametags_2d.add(self) + + self.setName('unnamed') + + self.m_contents = 3 + self.m_chat_contents = 0 + self.updateContents() + self.m_on = NametagGlobals._master_arrows_on + self.m_seq2d = 0 + + self.m_trans_vec = Vec3(0, 0, 0) + + def setVisible(self, value): + self.m_visible = value + self.updateContents() + + def manage(self, manager): + self.updateContents() + manager.managePopup(self) + + def unmanage(self, manager): + Nametag.unmanage(self, manager) + manager.unmanagePopup(self) + + def setObjectCode(self, objcode): + if self.m_group: + self.m_group.setObjectCode(objcode) + + def getObjectCode(self): + if self.m_group: + return self.m_group.getObjectCode() + + return 0 + + def getScore(self): + if self.m_group: + return 1000 - self.getDistance2() + + return 0 + + def getDistance2(self): + if self.m_avatar: + np = self.m_avatar + + else: + np = self.m_group.getAvatar() + + if np.isEmpty(): + return 0 + + return np.getPos(NametagGlobals._toon).lengthSquared() + + def considerVisible(self): + from .NametagGroup import NametagGroup + + v2 = 0 + do_update = True + if self.m_on != NametagGlobals._master_arrows_on: + self.m_on = NametagGlobals._master_arrows_on + v2 = 1 + + if self.m_seq2d == NametagGlobals._margin_prop_seq: + if not v2: + do_update = False + + else: + self.m_seq2d = NametagGlobals._margin_prop_seq + + if do_update: + self.updateContents() + + if not self.m_chat_contents: + return 0 + + result = self.m_group.m_nametag3d_flag != 2 + if NametagGlobals._onscreen_chat_forced and self.m_chat_contents & (Nametag.CSpeech | Nametag.CThought): + result = 1 + + self.m_group.setNametag3dFlag(0) + if result and self.m_group.getColorCode() in (NametagGroup.CCToonBuilding, + NametagGroup.CCSuitBuilding, + NametagGroup.CCHouseBuilding): + return self.getDistance2() < 1600 + + return result + + def updateContents(self): + self.stopFlash() + if self.m_group: + self.setName(self.m_group.getName()) + + else: + self.setName('unnamed') + + if self.m_copied_np: + self.m_copied_np.removeNode() + + if self.m_attached_np: + self.m_attached_np.removeNode() + + if self.m_arrow: + self.m_arrow.removeNode() + + if self.m_unknown_np: + self.m_unknown_np.removeNode() + + self.m_chat_contents = self.determineContents() + if not NametagGlobals._master_arrows_on: + self.m_chat_contents = self.m_chat_contents & ~1 + + if self.m_visible and self.isGroupManaged(): + v10 = self.m_chat_contents + if v10 & Nametag.CSpeech: + self.generateChat(NametagGlobals._speech_balloon_2d) + + elif v10 & Nametag.CThought: + self.generateChat(NametagGlobals._thought_balloon_2d) + + elif v10 & Nametag.CName: + self.generateName() + + def frameCallback(self): + if self.m_visible and self.m_popup_region: + self.m_seq = self.m_group.m_region_seq + + if self.m_group: + self.m_group.updateRegions() + + def rotateArrow(self): + if not self.m_arrow: + return + + if self.m_avatar: + np = self.m_avatar + + else: + np = self.m_group.getAvatar() + + if not np: + return + + relpos = np.getPos(NametagGlobals._camera) - NametagGlobals._toon.getPos(NametagGlobals._camera) + hpr = Vec3(0, 0, -math.atan2(relpos[1], relpos[0]) * 180 / math.pi) + scale = Vec3(0.5, 0.5, 0.5) + shear = Vec3(0, 0, 0) + + temp_mat_3 = Mat3() + composeMatrix(temp_mat_3, scale, shear, hpr) + arrow_mat = Mat4(temp_mat_3, self.m_trans_vec) + self.m_arrow.setMat(arrow_mat) + + def generateName(self): + v4 = self.getState() + v84 = Vec4(NametagGlobals.getNameFg(self.m_group.getColorCode(), v4)) + + # AP Teams Nametag hook. If we have an override for a color profile use it. + if self.usingColorProfile(): + v84 = self.getColorProfile().getColorFromState(self.getState()) + + v75 = Vec4(NametagGlobals.getNameBg(self.m_group.getColorCode(), v4)) + v75[3] = max(v75[3], NametagGlobals._min_2d_alpha) + v75[3] = min(v75[3], NametagGlobals._max_2d_alpha) + + v67 = NametagGlobals._card_pad[3] + self.m_group.m_name_frame[3] + v68 = self.m_group.m_name_frame[2] - NametagGlobals._card_pad[2] + + wordwrap = self.m_group.getNameWordwrap() + v17 = self.m_cell_width / wordwrap * 2.0 + v66 = 0.333 * (1.0 / v17) - (v68 + v67) * 0.5 + v18 = min(1.0 / v17 - v67, v66) + + v69 = Mat4(v17, 0, 0, 0, + 0, v17, 0, 0, + 0, 0, v17, 0, + 0, 0, v18 * v17, 1.0) + a3 = v69 + + if v75[3] != 0.0: + card = CardMaker('nametag') + card.setFrame(self.m_group.m_name_frame[0] - NametagGlobals._card_pad[0], + self.m_group.m_name_frame[1] + NametagGlobals._card_pad[1], + v68, v67) + card.setColor(v75) + if NametagGlobals._nametag_card: + card.setSourceGeometry(NametagGlobals._nametag_card.node(), + NametagGlobals._nametag_card_frame) + + self.m_attached_np = self.m_np.attachNewNode(card.generate()) + self.m_attached_np.setMat(v69) + if v75[3] != 1.0: + self.m_attached_np.setTransparency(1) + + if self.m_has_draw_order: + bin = config.GetString('nametag-fixed-bin', 'fixed') + self.m_attached_np.setBin(bin, self.m_draw_order) + + self.m_copied_np = self.m_group.copyNameTo(self.m_np) + self.m_copied_np.setMat(a3) + if self.m_has_draw_order: + bin = config.GetString('nametag-fixed-bin', 'fixed') + self.m_copied_np.setBin(bin, self.m_draw_order) + + self.m_copied_np.setColor(v84) + if v84[3] != 1.0: + self.m_copied_np.setTransparency(1) + + reducer = SceneGraphReducer() + reducer.applyAttribs(self.m_copied_np.node()) + reducer.applyAttribs(self.m_attached_np.node()) + + if NametagGlobals._arrow_model: + self.m_arrow = NametagGlobals._arrow_model.copyTo(self.m_np) + if self.m_has_draw_order: + bin = config.GetString('nametag-fixed-bin', 'fixed') + self.m_arrow.setBin(bin, self.m_draw_order) + + self.m_trans_vec = a3.xformPoint(Point3(0, 0, v68 - 1.0)) + + color = Vec4(NametagGlobals.getArrowColor(self.m_group.getColorCode())) + + # AP Teams Nametag hook. If we have an override for a color profile use it. + if self.usingColorProfile(): + color = self.getColorProfile().getColorFromState(PGButton.SInactive) + + self.m_arrow.setColor(color) + if color[3] != 1.0: + self.m_arrow.setTransparency(1) + + self.rotateArrow() + + elif self.m_arrow: + self.m_arrow.removeNode() + + v69 = self.m_np.getNetTransform().getMat() + v69 = a3 * v69 + + v77 = v69.xformPoint(Point3(self.m_group.m_name_frame[0] - NametagGlobals._card_pad[0], 0, v68)) + v80 = v69.xformPoint(Point3(self.m_group.m_name_frame[1] + NametagGlobals._card_pad[1], 0, v67)) + + frame = Vec4(v77[0], v80[0], v77[2], v80[2]) + self.setRegion(frame, 0) + + def generateChat(self, balloon): + v5 = self.getState() + text_color = Vec4(NametagGlobals.getChatFg(self.m_group.getColorCode(), v5)) + balloon_color = Vec4(NametagGlobals.getChatBg(self.m_group.getColorCode(), v5)) + + if self.m_group.m_chat_flags & CFQuicktalker: + balloon_color = Vec4(self.m_group.getQtColor()) + + balloon_color[3] = max(balloon_color[3], NametagGlobals._min_2d_alpha) + balloon_color[3] = min(balloon_color[3], NametagGlobals._max_2d_alpha) + + text = self.m_group.getChat() + if self.m_group.m_name: + text = '%s: %s' % (self.m_group.m_name, text) + + has_page_button = False + has_quit_button = False + if not self.m_group.m_has_timeout: + has_page_button = self.m_group.m_chat_flags & CFPageButton + if self.m_group.getPageNumber() >= self.m_group.getNumChatPages() - 1: + if self.m_group.m_chat_flags & CFQuitButton: + has_page_button = False + has_quit_button = True + + page_button = None + if has_page_button: + page_button = NametagGlobals.getPageButton(v5) + + elif has_quit_button: + page_button = NametagGlobals.getQuitButton(v5) + + reversed = self.m_group.m_chat_flags & CFReversed + new_button = [None] + balloon_result = balloon.generate(text, self.m_group.getChatFont(), self.m_wordwrap, + text_color, balloon_color, False, + self.m_has_draw_order, self.m_draw_order, + page_button, self.m_group.willHaveButton(), + reversed, new_button) + + self.m_unknown_np = self.m_np.attachNewNode(balloon_result) + + v88 = 8.0 # XXX THIS IS A GUESS + v49 = 2 * self.m_cell_width + a6 = v49 / (v88 + 1.0) + v50 = balloon.m_text_height * balloon.m_hscale + v85 = balloon.m_hscale * 5.0 + v88 = v50 * 0.5 + v113 = -(balloon.m_hscale * 0.5 + v85) + v51 = -(NametagGlobals._balloon_text_origin[2] + v88) + v118 = Mat4(a6, 0, 0, 0, + 0, a6, 0, 0, + 0, 0, a6, 0, + v113 * a6, 0, v51 * a6, 1.0) + + self.m_unknown_np.setMat(v118) + + reducer = SceneGraphReducer() + reducer.applyAttribs(self.m_unknown_np.node()) + + v66 = self.m_np.getNetTransform().getMat() + + # XXX THE LINES BELOW ARE A GUESS + v67 = v113 * a6 + v68 = v51 * a6 + v94 = v66.xformPoint(Point3(v67, 0.0, v68)) + v97 = v66.xformPoint(Point3(-v67, 0.0, -v68)) + + frame = Vec4(v94[0], v97[0], v94[2], v97[2]) + self.setRegion(frame, 0) + + def cullCallback(self, *args): + self.rotateArrow() + if self.m_visible and self.m_popup_region: + self.m_seq = self.m_group.getRegionSeq() diff --git a/libotp/nametag/Nametag3d.py b/libotp/nametag/Nametag3d.py new file mode 100644 index 0000000..e4ddc25 --- /dev/null +++ b/libotp/nametag/Nametag3d.py @@ -0,0 +1,405 @@ +import math + +from panda3d.core import * + +from . import NametagGlobals +from .Nametag import Nametag +from ._constants import * + + +class Nametag3d(Nametag, PandaNode): + def __init__(self): + Nametag.__init__(self, 10.5) + PandaNode.__init__(self, 'unnamed') + + self.m_np_360 = None + self.m_np_372 = None + self.m_np_balloon = None + + # self.cbNode = CallbackNode(self.getName() + '-cbNode') + # self.cbNode.setCullCallback(PythonCallbackObject(self.cullCallback)) + # self.addChild(self.cbNode) + NametagGlobals._active_nametags_3d.add(self) + + self.m_billboard_offset = 3.0 + + self.m_np_top = NodePath.anyPath(PandaNode('top')) + self.m_is_3d = 1 + self.m_field_396 = 0 + self.m_name_frame = Vec4(0, 0, 0, 0) + self.m_chat_contents = None + + self.setBounds(BoundingSphere((0, 0, 0), 2.0)) + + def setBillboardOffset(self, billboard_offset): + self.m_billboard_offset = billboard_offset + + def getBillboardOffset(self): + return self.m_billboard_offset + + def cullCallback(self, traverse_data): + if self.isGroupManaged(): + # sort = CullBinManager.getGlobalPtr().getBinSort(traverse_data._state.getBinIndex()) + # np = traverse_data._node_path.getNodePath() + traverser = traverse_data.getTrav() + np = NodePath.anyPath(self) + sort = CullBinManager.getGlobalPtr().getBinSort(traverser.getInitialState().getBinIndex()) + self.adjustToCamera(np, sort) + + def manage(self, manager): + self.m_np_top.reparentTo(NodePath.anyPath(self)) + # Nametag/chat geometry is emissive — never lit by scene lights. + # Priority 101 overrides render.setLight(_, 100) from OutdoorLighting. + self.m_np_top.setLightOff(101) + self.updateContents() + + def unmanage(self, manager): + self.m_np_top.detachNode() + Nametag.unmanage(self, manager) + + def updateContents(self): + self.stopFlash() + + if self.m_has_draw_order: + bin = config.GetString('nametag-fixed-bin', 'fixed') + self.m_np_top.setBin(bin, self.m_draw_order) + + else: + self.m_np_top.clearBin() + + if self.m_group: + self.setName(self.m_group.getName()) + + else: + self.setName('unnamed') + + if self.m_np_360: + self.m_np_360.removeNode() + + if self.m_np_372: + self.m_np_372.removeNode() + + if self.m_np_balloon: + self.m_np_balloon.removeNode() + + self.m_np_top.node().removeAllChildren() + + self.m_chat_contents = self.determineContents() + if self.isGroupManaged(): + if self.m_chat_contents & 2: + self.generateChat(NametagGlobals._speech_balloon_3d) + + elif self.m_chat_contents & 4: + self.generateChat(NametagGlobals._thought_balloon_3d) + + elif self.m_chat_contents & 1: + self.generateName() + + def release(self, arg): + if arg.getButton() == MouseButton.one(): + self.setState(PGButton.SRollover) + if self.m_group: + self.m_group.click() + + def generateChat(self, balloon): + v5 = self.getState() + text_color = Vec4(NametagGlobals.getChatFg(self.m_group.getColorCode(), v5)) + balloon_color = Vec4(NametagGlobals.getChatBg(self.m_group.getColorCode(), v5)) + + if self.m_group.m_chat_flags & CFQuicktalker: + balloon_color = Vec4(self.m_group.getQtColor()) + + text = self.m_group.getChat() + has_page_button = False + has_quit_button = False + if not self.m_group.m_has_timeout: + has_page_button = self.m_group.m_chat_flags & CFPageButton + if self.m_group.getPageNumber() >= self.m_group.getNumChatPages() - 1: + if self.m_group.m_chat_flags & CFQuitButton: + has_page_button = False + has_quit_button = True + + page_button = None + if has_page_button: + page_button = NametagGlobals.getPageButton(v5) + + elif has_quit_button: + page_button = NametagGlobals.getQuitButton(v5) + + reversed = self.m_group.m_chat_flags & CFReversed + new_button = [None] + balloon_result = balloon.generate(text, self.m_group.getChatFont(), self.m_wordwrap, + text_color, balloon_color, self.m_is_3d, + self.m_has_draw_order, self.m_draw_order, + page_button, self.m_group.willHaveButton(), + reversed, new_button) + + self.m_np_balloon = self.m_np_top.attachNewNode(balloon_result) + if new_button[0]: + self.startFlash(new_button[0]) + + self.m_name_frame = balloon.m_text_frame + self.m_field_396 = 1 + + def generateName(self): + v4 = self.getState() + v56 = Vec4(NametagGlobals.getNameFg(self.m_group.getColorCode(), v4)) + + # Archipelago Teams Nametag integration removed + + v54 = Vec4(NametagGlobals.getNameBg(self.m_group.getColorCode(), v4)) + + + self.m_name_frame = Vec4(*self.m_group.getNameFrame()) + self.m_name_frame[0] -= NametagGlobals._card_pad[0] + self.m_name_frame[1] += NametagGlobals._card_pad[1] + self.m_name_frame[2] -= NametagGlobals._card_pad[2] + self.m_name_frame[3] += NametagGlobals._card_pad[3] + self.m_field_396 = 1 + + v47 = None + if v54[3] != 0.0: + card = CardMaker('nametag') + card.setFrame(self.m_name_frame) + card.setColor(v54) + if NametagGlobals._nametag_card: + card.setSourceGeometry(NametagGlobals._nametag_card.node(), + NametagGlobals._nametag_card_frame) + + self.m_np_372 = self.m_np_top.attachNewNode(card.generate()) + self.m_np_372.setTransparency(1) + v47 = self.m_np_372.find('**/+GeomNode') + + label86 = False + if self.m_is_3d: + if self.m_group.m_name_icon: + self.m_group.m_name_icon.instanceTo(self.m_np_top) + + if v47: + self.m_np_360 = self.m_group.copyNameTo(v47) + self.m_np_360.setDepthWrite(0) + self.m_np_360.setY(-0.01) # Panda3D 1.10 hack to prevent z-fighting. + v47.node().setEffect(DecalEffect.make()) + + else: + label86 = True + + else: + label86 = True + + if label86: + self.m_np_360 = self.m_group.copyNameTo(self.m_np_top) + if self.m_has_draw_order: + bin = config.GetString('nametag-fixed-bin', 'fixed') + self.m_name_icon.setBin(bin, self.m_draw_order + 1) + self.m_np_360.setBin(bin, self.m_draw_order + 2) + + self.m_np_360.setColor(v56) + if v56[3] != 1.0: + self.m_np_360.setTransparency(1) + + def adjustToCamera(self, np, sort): + if self.m_is_3d: + lens = NametagGlobals._camera.node().getLens(0) + if self.m_avatar or not self.m_group: + v131 = self.m_avatar + + else: + v131 = self.m_group.m_avatar + + v130 = NametagGlobals._camera.getTransform(np) + v25 = v130.getMat() + v204 = v25.xformVec(Vec3.up()) + v203 = v25.xformVec(Vec3.forward()) + + v193 = Mat3() + lookAt(v193, Vec3(v203), Vec3(v204)) + v177 = Mat4(v193) + v177_3_0 = v177[3][0] + v177_3_1 = v177[3][1] + + a3 = np.getTransform(NametagGlobals._camera).getMat() + v122 = a3[3][1] + v30 = max(v122, 0.1) + v31 = v30 * 0.02 + v121 = (v31 ** 0.5) * NametagGlobals.getGlobalNametagScale() * 0.56 + if self.m_billboard_offset == 0.0: + v42 = 0 + + else: + v32 = v25[0][1] + v33 = v25[0][0] + v136 = v25[0][2] + v134 = v33 + v127 = self.m_billboard_offset + v144 = math.sqrt(v136 * v136 + v32 * v32 + v134 * v134) + v129 = self.m_billboard_offset / v144 + if v122 > 0.0: + if isinstance(lens, PerspectiveLens): + v37 = lens.getNear() + 0.001 + if v122 - v129 < v37: + v129 = v122 - v37 + v127 = v129 * v144 + + v121 = (v122 - v129) / v122 * v121 + + v38 = v25[3][0] + v39 = v25[3][1] + v126 = v25[3][2] + v125 = v39 + v129 = v126 * v126 + v39 * v39 + v38 * v38 + if v129 == 0.0: + v125 = 0.0 + v38 = 0.0 + v126 = 0.0 + + else: + v40 = v129 - 1.0 + if abs(v40) > 0.0: # if ( v40 >= 1.0e-12 || v40 <= -1.0e-12 ) NOT ALMOST ZERO + v41 = 1.0 / math.sqrt(v129) + v38 *= v41 + v125 *= v41 + v126 *= v41 + + v126 *= v127 + v177_3_0 = v38 * v127 + v177_3_1 = v125 * v127 + v42 = v126 + + v205 = Mat4(v177[0][0] * v121, v177[0][1] * v121, v177[0][2] * v121, v177[0][3] * v121, + v177[1][0] * v121, v177[1][1] * v121, v177[1][2] * v121, v177[1][3] * v121, + v177[2][0] * v121, v177[2][1] * v121, v177[2][2] * v121, v177[2][3] * v121, + v177_3_0, v177_3_1, v42, v177[3][3]) + self.m_np_top.setMat(v205) + + v51 = 0 + if self.displayAsActive(): + if not self.m_chat_contents & (2 | 4): + v51 = 1 + + elif not self.m_group: + v51 = 1 + + elif self.m_group.m_has_timeout: + v51 = 1 + + elif not self.m_group.willHaveButton(): + v51 = 1 + + elif self.m_group.getPageNumber() >= self.m_group.getNumChatPages() - 1: + v51 = 1 + + v123 = 0 + sorta = 0 + frame = Vec4(0, 0, 0, 0) + v150 = lens.getProjectionMat() + + if v51: + v138, v139, v140 = v205.xformVec(Vec3(-2.5, 0.0, 1.0)) + v124, v125, v126 = v205.xformVec(Vec3(2.5, 0.0, 1.0)) + + v121 = np.getTransform(v131) + if v121.isInvalid(): + return + + v64 = v121.getMat() + v138, v139, v140 = v64.xformPoint(Point3(v138, v139, v140)) + v124, v125, v126 = v64.xformPoint(Point3(v124, v125, v126)) + + v122 = v131.getTransform(NametagGlobals._camera) + if v122.isInvalid(): + return + + v124, v125, v126 = v122.getMat().xformPoint(Point3(v124, v125, v126)) + v134, v135, v136 = v122.getMat().xformPoint(Point3(v138, v139, 0.0)) + + a2 = v150.xform(Vec4(v124, v125, v126, 1.0)) + frame = v150.xform(Vec4(v134, v135, v136, 1.0)) + + if a2[3] <= 0.0 or frame[3] <= 0.0: + self.m_group.m_nametag3d_flag &= (self.m_group.m_nametag3d_flag <= 0) - 1 + self.deactivate() + return + + v123 = 1 + v133 = 1.0 / frame[3] + v128 = 1.0 / a2[3] + frame = Vec4(frame[0] * v133, a2[0] * v128, frame[1] * v133, a2[1] * v128) + + v89 = 0 + if self.m_field_396: + v124, v125, v126 = v205.xformPoint(Point3(self.m_name_frame[0] - 0.5, 0.0, self.m_name_frame[2] - 1.0)) + v138, v139, v140 = v205.xformPoint(Point3(self.m_name_frame[1] + 0.5, 0.0, self.m_name_frame[3] + 1.0)) + + v124, v125, v126 = a3.xformPoint(Point3(v124, v125, v126)) + v138, v139, v140 = a3.xformPoint(Point3(v138, v139, v140)) + + a2 = v150.xform(Vec4(v138, v139, v140, 1.0)) + v134, v135, v136, v137 = v150.xform(Vec4(v124, v125, v126, 1.0)) + + if v137 <= 0.0 or a2[3] <= 0.0: + v89 = 1 + + else: + v133 = 1.0 / v137 + v109 = 1.0 / a2[3] + v110 = v109 * a2[1] + v127 = v133 * v135 + v131 = v109 * a2[0] + v111 = v133 * v134 + v146 = v111 + if v111 < -1.0 or v131 > 1.0 or v127 < -1.0 or v110 > 1.0: + v89 = 1 + + if v123: + if frame[3] > v110: + v110 = frame[3] + if frame[2] >= v127: + v115 = v127 + else: + v115 = frame[2] + if frame[1] <= v131: + v116 = v131 + else: + v116 = frame[1] + if frame[0] >= v146: + frame[0] = v146 + + frame[1] = v116 + frame[2] = v115 + + else: + frame[0] = v146 + frame[1] = v131 + frame[2] = v127 + v123 = 1 + + frame[3] = v110 + sorta = int(v125 * -100.0) + + if v123 and self.displayAsActive(): + self.setRegion(frame, sorta) + + v118 = self.m_group.m_nametag3d_flag + if v89: + v118 = max(v118, 1) + + elif v118 <= 2: + v118 = 2 + + self.m_group.setNametag3dFlag(v118) + return + + self.m_group.incrementNametag3dFlag(2) + if not self.displayAsActive(): + return + + if not self.m_field_396: + return + + v12 = np.getNetTransform().getMat() + v124 = v12.xformPoint(Point3(self.m_name_frame[0] - 0.5, 0, self.m_name_frame[2] - 1.0)) + v16 = v12.xformPoint(Point3(self.m_name_frame[1] + 0.5, 0, self.m_name_frame[3] + 1.0)) + + v131 = Vec4(v124[0], v16[0], v124[2], v16[2]) + self.setRegion(v131, sort) diff --git a/libotp/nametag/NametagFloat2d.py b/libotp/nametag/NametagFloat2d.py new file mode 100644 index 0000000..3c48f3a --- /dev/null +++ b/libotp/nametag/NametagFloat2d.py @@ -0,0 +1,8 @@ +from .Nametag3d import Nametag3d + + +class NametagFloat2d(Nametag3d): + def __init__(self): + Nametag3d.__init__(self) + self.m_is_3d = False + self.updateContents() diff --git a/libotp/nametag/NametagFloat3d.py b/libotp/nametag/NametagFloat3d.py new file mode 100644 index 0000000..0c7bc28 --- /dev/null +++ b/libotp/nametag/NametagFloat3d.py @@ -0,0 +1,6 @@ +from .Nametag3d import Nametag3d + + +class NametagFloat3d(Nametag3d): + def __init__(self): + Nametag3d.__init__(self) diff --git a/libotp/nametag/NametagGlobals.py b/libotp/nametag/NametagGlobals.py new file mode 100644 index 0000000..e60abf0 --- /dev/null +++ b/libotp/nametag/NametagGlobals.py @@ -0,0 +1,446 @@ +from panda3d.core import * + +_speech_balloon_3d = None + + +def setSpeechBalloon3d(speech_balloon_3d): + global _speech_balloon_3d + _speech_balloon_3d = speech_balloon_3d + + +def getSpeechBalloon3d(): + return _speech_balloon_3d + + +_global_nametag_scale = 1.0 + + +def setGlobalNametagScale(global_nametag_scale): + global _global_nametag_scale + _global_nametag_scale = global_nametag_scale + + +def getGlobalNametagScale(): + return _global_nametag_scale + + +_camera = NodePath() + + +def setCamera(camera): + global _camera + _camera = camera + + +def getCamera(): + return _camera + + +_master_nametags_active = True + + +def setMasterNametagsActive(master_nametags_active): + global _master_nametags_active + _master_nametags_active = master_nametags_active + + +def getMasterNametagsActive(): + return _master_nametags_active + + +_min_2d_alpha = 0.0 + + +def setMin2dAlpha(min_2d_alpha): + global _min_2d_alpha + global _margin_prop_seq + _min_2d_alpha = min_2d_alpha + _margin_prop_seq += 1 + + +def getMin2dAlpha(): + return _min_2d_alpha + + +_arrow_color = [ + Vec4(1.0, 0.40000001, 0.2, 1.0), + Vec4(1.0, 0.40000001, 0.2, 1.0), + Vec4(1.0, 0.40000001, 0.2, 1.0), + Vec4(1.0, 0.40000001, 0.2, 1.0), + Vec4(0.30000001, 0.60000002, 1.0, 1.0), + Vec4(0.55000001, 0.55000001, 0.55000001, 1.0), + Vec4(0.30000001, 0.60000002, 1.0, 1.0), + Vec4(0.30000001, 0.69999999, 0.30000001, 1.0), + Vec4(0.30000001, 0.30000001, 0.69999999, 1.0) +] + + +def getArrowColor(index): + return _arrow_color[index] + + +_mouse_watcher = None + + +def setMouseWatcher(mouse_watcher): + global _mouse_watcher + _mouse_watcher = mouse_watcher + + +def getMouseWatcher(): + return _mouse_watcher + + +_master_arrows_on = True + + +def setMasterArrowsOn(master_arrows_on): + global _master_arrows_on + _master_arrows_on = master_arrows_on + + +def getMasterArrowsOn(): + return _master_arrows_on + + +_toon = NodePath() + + +def setToon(toon): + global _toon + _toon = toon + + +def getToon(): + return _toon + + +_master_nametags_visible = True + + +def setMasterNametagsVisible(master_nametags_visible): + global _master_nametags_visible + _master_nametags_visible = master_nametags_visible + + +def getMasterNametagsVisible(): + return _master_nametags_visible + + +_thought_balloon_2d = None + + +def setThoughtBalloon2d(thought_balloon_2d): + global _thought_balloon_2d + _thought_balloon_2d = thought_balloon_2d + + +def getThoughtBalloon2d(): + return _thought_balloon_2d + + +_max_2d_alpha = 0.6 + + +def setMax2dAlpha(max_2d_alpha): + global _max_2d_alpha + global _margin_prop_seq + _max_2d_alpha = max_2d_alpha + _margin_prop_seq += 1 + + +def getMax2dAlpha(): + return _max_2d_alpha + + +_onscreen_chat_forced = False + + +def setOnscreenChatForced(onscreen_chat_forced): + global _onscreen_chat_forced + _onscreen_chat_forced = onscreen_chat_forced + + +def getOnscreenChatForced(): + return _onscreen_chat_forced + + +_nametag_card = NodePath() + + +def setNametagCard(nametag_card, nametag_card_frame): + global _nametag_card + global _nametag_card_frame + _nametag_card = nametag_card + _nametag_card_frame = nametag_card_frame + + +def getNametagCard(): + return _nametag_card + + +_nametag_card_frame = Vec4(0, 0, 0, 0) + + +def getNametagCardFrame(): + return _nametag_card_frame + + +_rollover_sound = None + + +def setRolloverSound(rollover_sound): + global _rollover_sound + _rollover_sound = rollover_sound + + +def getRolloverSound(): + return _rollover_sound + + +_speech_balloon_2d = None + + +def setSpeechBalloon2d(speech_balloon_2d): + global _speech_balloon_2d + _speech_balloon_2d = speech_balloon_2d + + +def getSpeechBalloon2d(): + return _speech_balloon_2d + + +_text_node = TextNode('nametag') + + +def getTextNode(): + return _text_node + + +_click_sound = None + + +def setClickSound(click_sound): + global _click_sound + _click_sound = click_sound + + +def getClickSound(): + return _click_sound + + +_quit_button = [NodePath(), NodePath(), NodePath(), NodePath()] + + +def setQuitButton(state, quit_button): + global _quit_button + _quit_button[state] = quit_button + + +def getQuitButton(state): + return _quit_button[state] + + +_arrow_model = NodePath() + + +def setArrowModel(arrow_model): + global _arrow_model + _arrow_model = arrow_model + + +def getArrowModel(): + return _arrow_model + + +_thought_balloon_3d = None + + +def setThoughtBalloon3d(thought_balloon_3d): + global _thought_balloon_3d + _thought_balloon_3d = thought_balloon_3d + + +def getThoughtBalloon3d(): + return _thought_balloon_3d + + +_name_bg = [ + # CCNormal + Vec4(0.8, 0.8, 0.8, 0.5), + Vec4(0.2, 0.2, 0.2, 0.6), + Vec4(1, 1, 1, 1), + Vec4(0.8, 0.8, 0.8, 0.5), + + # CCNoChat + Vec4(1, 1, 1, 0.5), + Vec4(0.2, 0.2, 0.2, 0.6), + Vec4(1, 1, 1, 1), + Vec4(0.8, 0.8, 0.8, 0.5), + + # CCNonPlayer + Vec4(1, 1, 1, 0.5), + Vec4(0.2, 0.2, 0.2, 0.6), + Vec4(1, 1, 1, 1), + Vec4(0.8, 0.8, 0.8, 0.5), + + # CCSuit + Vec4(0.8, 0.8, 0.8, 0.5), + Vec4(0.2, 0.2, 0.2, 0.6), + Vec4(1, 1, 1, 1), + Vec4(0.8, 0.8, 0.8, 0.5), + + # CCToonBuilding + Vec4(0.8, 0.8, 0.8, 0.5), + Vec4(0.2, 0.2, 0.2, 0.6), + Vec4(1, 1, 1, 1), + Vec4(0.8, 0.8, 0.8, 0.5), + + # CCSuitBuilding + Vec4(0.8, 0.8, 0.8, 0.5), + Vec4(0.2, 0.2, 0.2, 0.6), + Vec4(1, 1, 1, 1), + Vec4(0.8, 0.8, 0.8, 0.5), + + # CCHouseBuilding + Vec4(0.8, 0.8, 0.8, 0.5), + Vec4(0.2, 0.2, 0.2, 0.6), + Vec4(1, 1, 1, 1), + Vec4(0.8, 0.8, 0.8, 0.5), + + # CCSpeedChat + Vec4(1, 1, 1, 0.5), + Vec4(0.2, 0.2, 0.2, 0.6), + Vec4(1, 1, 1, 1), + Vec4(0.8, 0.8, 0.8, 0.5), + + # CCFreeChat + Vec4(0.8, 0.8, 0.8, 0.5), + Vec4(0.2, 0.2, 0.2, 0.6), + Vec4(1, 1, 1, 1), + Vec4(0.8, 0.8, 0.8, 0.5) +] + + +def getNameBg(color_code, state): + return _name_bg[4 * color_code + state] + + +_page_button = [NodePath(), NodePath(), NodePath(), NodePath()] + + +def setPageButton(state, page_button): + global _page_button + _page_button[state] = page_button + + +def getPageButton(state): + return _page_button[state] + + +_name_fg = [ + # CCNormal + Vec4(0, 0, 1, 1), + Vec4(0.5, 0.5, 1, 1), + Vec4(0.5, 0.5, 1, 1), + Vec4(0.3, 0.3, 0.7, 1), + + # CCNoChat + Vec4(0.8, 0.4, 0, 1), + Vec4(1, 0.5, 0.5, 1), + Vec4(1, 0.5, 0, 1), + Vec4(0.6, 0.4, 0.2, 1), + + # CCNonPlayer + Vec4(0.8, 0.4, 0, 1), + Vec4(1, 0.5, 0.5, 1), + Vec4(1, 0.5, 0, 1), + Vec4(0.6, 0.4, 0.2, 1), + + # CCSuit + Vec4(0, 0, 0, 1), + Vec4(1, 1, 1, 1), + Vec4(0.5, 0.5, 0.5, 1), + Vec4(0.2, 0.2, 0.2, 1), + + # CCToonBuilding + Vec4(0, 0, 0, 1), + Vec4(1, 1, 1, 1), + Vec4(0.5, 0.5, 0.5, 1), + Vec4(0.3, 0.6, 1, 1), + + # CCSuitBuilding + Vec4(0, 0, 0, 1), + Vec4(1, 1, 1, 1), + Vec4(0.5, 0.5, 0.5, 1), + Vec4(0.55, 0.55, 0.55, 1), + + # CCHouseBuilding + Vec4(0, 0, 0, 1), + Vec4(1, 1, 1, 1), + Vec4(0.5, 0.5, 0.5, 1), + Vec4(0.3, 0.6, 1, 1), + + # CCSpeedChat + Vec4(0, 0.6, 0.2, 1), + Vec4(0, 0.6, 0.2, 1), + Vec4(0, 1, 0.5, 1), + Vec4(0.1, 0.4, 0.2, 1), + + # CCFreeChat + Vec4(0.3, 0.3, 0.7, 1), + Vec4(0.2, 0.2, 0.5, 1), + Vec4(0.5, 0.5, 1, 1), + Vec4(0.3, 0.3, 0.7, 1) +] + + +def getNameFg(color_code, state): + return _name_fg[4 * color_code + state] + + +def getNameWordwrap(): + return 7.5 + + +_card_pad = Vec4(0.1, 0.1, 0.1, 0) + + +def getCardPad(): + return _card_pad + + +_balloon_modulation_color = Vec4(1.0, 1.0, 1.0, 1.0) + + +def setBalloonModulationColor(balloon_modulation_color): + global _balloon_modulation_color + _balloon_modulation_color = balloon_modulation_color + + +def getBalloonModulationColor(): + return _balloon_modulation_color + + +def getChatFg(color_code, state): + return [Vec4(0.0, 0.0, 0.0, 1.0), + Vec4(1.0, 0.5, 0.5, 1.0), + Vec4(0.0, 0.6, 0.6, 1.0), + Vec4(0.0, 0.0, 0.0, 1.0)][state] + + +def getChatBg(color_code, state): + return [Vec4(1.0, 1.0, 1.0, 1.0), + Vec4(1.0, 1.0, 1.0, 1.0), + Vec4(1.0, 1.0, 1.0, 1.0), + Vec4(1.0, 1.0, 1.0, 1.0)][state] + + +_margin_prop_seq = 0 +_default_qt_color = Vec4(0.8, 0.8, 1, 1) +_balloon_text_origin = Point3(1.0, 0, 2.0) + +import weakref +_active_nametags_2d = weakref.WeakSet() +_active_nametags_3d = weakref.WeakSet() +_active_whisper_popups = weakref.WeakSet() diff --git a/libotp/nametag/NametagGroup.py b/libotp/nametag/NametagGroup.py new file mode 100644 index 0000000..4105d1f --- /dev/null +++ b/libotp/nametag/NametagGroup.py @@ -0,0 +1,427 @@ +from panda3d.core import * + +from .ColorProfile import ColorProfile, GRAY +from . import NametagGlobals +from .Nametag2d import Nametag2d +from .Nametag3d import Nametag3d +from ._constants import * + + +class NametagGroup: + CCNormal = 0 + CCNoChat = 1 + CCNonPlayer = 2 + CCSuit = 3 + CCToonBuilding = 4 + CCSuitBuilding = 5 + CCHouseBuilding = 6 + CCSpeedChat = 7 + CCFreeChat = 8 + + _unique_index = 0 + + def __init__(self): + self.m_nametags = [] + + self.m_name_font = None + self.m_chat_font = None + + self.m_avatar = None + self.m_node = None + + self.m_name = '' + self.m_display_name = '' + + self.m_chat_pages = [] + self.m_stomp_text = '' + self.m_unique_name = '' + + self.m_region_seq = 0 + + self.m_name_icon = NodePath.anyPath(PandaNode('icon')) + self.m_name_frame = Vec4(0, 0, 0, 0) + + self.m_wordwrap = -1.0 + self.m_color_code = 0 + self.m_qt_color = Vec4(NametagGlobals._default_qt_color) + self.m_balloon_color = Vec4(NametagGlobals._balloon_modulation_color) + self.m_shadow = (0, 0) + self.m_has_shadow = False + + self.m_timeout = 0.0 + self.m_timeout_start = 0.0 + self.m_has_timeout = False + self.m_stomp_time = 0.0 + self.m_stomp_chat_flags = None + self.m_chat_flags = 0 + self.m_page_number = 0 + self.m_stomp_delay = 0.5 + self.m_chat_stomp = 0 + + self.m_unique_name = 'nametag-%d' % NametagGroup._unique_index + NametagGroup._unique_index += 1 + + self.m_object_code = 0 + self.m_nametag3d_flag = 0 + self.m_manager = None + + self.m_region_seq += 1 + + self.m_contents = CFSpeech | CFThought | CFQuicktalker + self.m_is_active = 1 + self.m_active = NametagGlobals._master_nametags_active + self.m_visible = NametagGlobals._master_nametags_visible + + self.m_tag2d = Nametag2d() + self.m_tag3d = Nametag3d() + self.addNametag(self.m_tag2d) + self.addNametag(self.m_tag3d) + + def setFont(self, font): + self.setNameFont(font) + self.setChatFont(font) + + def setNameFont(self, font): + self.m_name_font = font + + def getNameFont(self): + return self.m_name_font + + def setChatFont(self, font): + self.m_chat_font = font + + def getChatFont(self): + return self.m_chat_font + + def setAvatar(self, avatar): + self.m_avatar = avatar + + def getAvatar(self): + return self.m_avatar + + def setNameIcon(self, icon): + self.m_name_icon = icon + + def getNameIcon(self): + return self.m_name_icon + + def setColorCode(self, code): + self.m_color_code = code + + def getColorCode(self): + return self.m_color_code + + def setContents(self, contents): + self.m_contents = contents + + def getContents(self): + return self.m_contents + + def setDisplayName(self, name): + self.m_display_name = name + + if name and self.m_name_font: + text_node = NametagGlobals.getTextNode() + text_node.setFont(self.m_name_font) + text_node.setWordwrap(self.getNameWordwrap()) + text_node.setAlign(TextNode.ACenter) + text_node.setText(name) + gen = text_node.generate() + self.m_node = gen + self.m_name_frame = text_node.getCardActual() + + if self.m_has_shadow: + self.m_node = PandaNode('name') + self.m_node.addChild(gen) + + pos = Point3(self.m_shadow[0], 0, -self.m_shadow[1]) + attached = NodePath.anyPath(self.m_node).attachNewNode(gen.copySubgraph()) + attached.setPos(pos) + attached.setColor(0, 0, 0, 1) + + else: + self.m_node = None + + self.updateContentsAll() + + def getDisplayName(self): + return self.m_display_name + + def setName(self, name): + self.m_name = name + self.setDisplayName(name) + + def getName(self): + return self.m_name + + def getNameFrame(self): + return self.m_name_frame + + def setNameWordwrap(self, wordwrap): + self.m_wordwrap = wordwrap + self.setDisplayName(self.m_display_name) + + def getNameWordwrap(self): + if self.m_wordwrap > 0.0: + return self.m_wordwrap + + wordwrap = NametagGlobals.getNameWordwrap() + return {self.CCNoChat: 7.8, + self.CCToonBuilding: 8.5, + self.CCSuitBuilding: 8.5, + self.CCHouseBuilding: 10.0}.get(self.m_color_code, wordwrap) + + def getNametag(self, index): + return self.m_nametags[index] + + def getNametag2d(self): + return self.m_tag2d + + def getNametag3d(self): + return self.m_tag3d + + def setNametag3dFlag(self, flag): + self.m_nametag3d_flag = flag + + def getNametag3dFlag(self): + return self.m_nametag3d_flag + + def getNumChatPages(self): + return len(self.m_chat_pages) + + def getNumNametags(self): + return len(self.m_nametags) + + def setObjectCode(self, code): + self.m_object_code = code + + def getObjectCode(self): + return self.m_object_code + + def setPageNumber(self, page): + if self.m_page_number == page: + return + + self.m_page_number = page + if self.willHaveButton(): + self.m_timeout_start = 0.0 + self.m_has_timeout = False + + self.updateContentsAll() + + def getPageNumber(self): + return self.m_page_number + + def getBalloonModulationColor(self): + return self.m_balloon_color + + def setQtColor(self, color): + self.m_qt_color = color + + def getQtColor(self): + return self.m_qt_color + + def getRegionSeq(self): + return self.m_region_seq + + def setShadow(self, shadow): + self.m_shadow = shadow + + def getShadow(self): + return self.m_shadow + + def getStompDelay(self): + return self.m_stomp_delay + + def getStompText(self): + return self.m_stomp_text + + def setUniqueId(self, name): + self.m_unique_name = name + + def getUniqueId(self): + return self.m_unique_name + + def hasButton(self): + if self.m_has_timeout: + return False + + return self.willHaveButton() + + def hasNoQuitButton(self): + return (not self.m_has_timeout) and self.m_chat_flags & CFSpeech + + def hasQuitButton(self): + return (not self.m_has_timeout) and self.m_chat_flags & CFQuitButton + + def hasPageButton(self): + return (not self.m_has_timeout) and self.m_chat_flags & CFPageButton + + def hasShadow(self): + return self.m_has_shadow + + def clearShadow(self): + self.m_has_shadow = False + + def incrementNametag3dFlag(self, flag): + self.m_nametag3d_flag = max(self.m_nametag3d_flag, flag) + + def isManaged(self): + return self.m_manager is not None + + def manage(self, manager): + if not self.m_manager: + self.m_manager = manager + for nametag in self.m_nametags: + nametag.manage(manager) + + def unmanage(self, manager): + if self.m_manager: + self.m_manager = None + for nametag in self.m_nametags: + nametag.unmanage(manager) + + def addNametag(self, nametag): + if nametag.m_group: + print('Attempt to add %s twice to %s.' % (nametag.__class__.__name__, self.m_name)) + return + + nametag.m_group = self + nametag.updateContents() + self.m_nametags.append(nametag) + + if self.m_manager: + nametag.manage(self.m_manager) + + def removeNametag(self, nametag): + if not nametag.m_group: + print('Attempt to removed %s twice from %s.' % (nametag.__class__.__name__, self.m_name)) + return + + if self.m_manager: + nametag.unmanage(self.m_manager) + + nametag.m_group = None + nametag.updateContents() + self.m_nametags.remove(nametag) + + def setActive(self, active): + self.m_is_active = active + + def isActive(self): + return self.m_active + + def updateContentsAll(self): + for nametag in self.m_nametags: + nametag.updateContents() + + def updateRegions(self): + for nametag in self.m_nametags: + nametag.updateRegion(self.m_region_seq) + + self.m_region_seq += 1 + + now = globalClock.getFrameTime() + if self.m_stomp_time < now and self.m_chat_stomp > 1: + self.m_chat_stomp = 0 + self.setChat(self.m_stomp_text, self.m_stomp_chat_flags, self.m_page_number) + + if self.m_chat_flags & CFTimeout and now >= self.m_timeout: + self.clearChat() + self.m_chat_stomp = 0 + + v7 = False + if self.m_has_timeout and now >= self.m_timeout_start: + self.m_has_timeout = 0 + v7 = True + + if self.m_active != NametagGlobals._master_nametags_active: + self.m_active = NametagGlobals._master_nametags_active + v7 = True + + if self.m_visible == NametagGlobals._master_nametags_visible: + if not v7: + return + + else: + self.m_visible = NametagGlobals._master_nametags_visible + + self.updateContentsAll() + + def willHaveButton(self): + return self.m_chat_flags & (CFPageButton | CFQuitButton) + + def setChat(self, chat, chat_flags, page_number=0): + self.m_chat_flags = chat_flags + self.m_page_number = page_number + + now = globalClock.getFrameTime() + must_split = True + if chat_flags and chat: + self.m_chat_stomp += 1 + if self.m_chat_stomp >= 2 and self.m_stomp_delay >= 0.05: + self.m_stomp_text = chat + self.m_stomp_chat_flags = self.m_chat_flags + self.m_stomp_time = now + self.m_stomp_delay + self.m_chat_flags = 0 + must_split = False + + else: + self.m_chat_flags = 0 + self.m_chat_stomp = 0 + must_split = False + + if must_split: + self.m_chat_pages = chat.split('\x07') + else: + self.m_chat_pages = [] + + if self.m_chat_flags & CFTimeout and self.m_stomp_time < now: + timeout = len(chat) * 0.5 + timeout = min(12.0, max(timeout, 4.0)) + self.m_timeout = timeout + now + + if self.willHaveButton(): + self.m_has_timeout = True + self.m_timeout_start = now + 0.2 + + else: + self.m_has_timeout = False + self.m_timeout_start = 0.0 + + self.updateContentsAll() + + def getChat(self): + if self.m_chat_pages: + return self.m_chat_pages[self.m_page_number] + + return '' + + def clearChat(self): + self.setChat('', 0, 0) + + def getChatStomp(self): + return self.m_chat_stomp + + def clearAuxNametags(self): + for nametag in self.nametags[:]: + if nametag not in (self.m_tag2d, self.m_tag3d): + self.removeNametag(nametag) + + def click(self): + messenger.send(self.m_unique_name) + + def copyNameTo(self, to): + return to.attachNewNode(self.m_node.copySubgraph()) + + def displayAsActive(self): + if self.m_is_active and NametagGlobals._master_nametags_active: + return 1 + + return self.hasButton() + + def frameCallback(self): + # This should be in Nametag2d + # I have no idea where libotp called it + # so I'm doing it in MarginManager.update + self.updateRegions() diff --git a/libotp/nametag/WhisperGlobals.py b/libotp/nametag/WhisperGlobals.py new file mode 100644 index 0000000..ebb82fa --- /dev/null +++ b/libotp/nametag/WhisperGlobals.py @@ -0,0 +1,96 @@ +from enum import IntEnum, auto + +from panda3d.core import Vec4 + +from .ColorProfile import ColorProfile, GRAY + + +class WhisperType(IntEnum): + + WTNormal = auto() + WTQuickTalker = auto() + WTSystem = auto() + WTBattleSOS = auto() + WTEmote = auto() + WTToontownBoardingGroup = auto() + WTMagicWord = auto() + + +# Defines some default color profiles from a whisper type. +# Overrides can however be defined when calling localAvatar.displayWhisper() +# Maps whisper type -> (ColorProfile(text), ColorProfile(background)) +WHISPER_COLORS = { + + # Normal whisper popups + WhisperType.WTNormal: ( + ColorProfile(Vec4(0.0, 0.0, 0.0, 1.0), Vec4(1.0, 0.5, 0.5, 1.0), Vec4(0.0, 0.0, 0.0, 1.0),Vec4(0.0, 0.0, 0.0, 1.0)), + ColorProfile(Vec4(0.3, 0.6, 0.8, 0.6), Vec4(1.0, 1.0, 1.0, 1.0), Vec4(0.4, 0.8, 1.0, 1.0),Vec4(0.3, 0.6, 0.8, 0.6)), + ), + + # Toons whispering to each other + WhisperType.WTQuickTalker: ( + ColorProfile(Vec4(0.0, 0.0, 0.0, 1.0), Vec4(1.0, 0.5, 0.5, 1.0), Vec4(0.0, 0.0, 0.0, 1.0), Vec4(0.0, 0.0, 0.0, 1.0)), + ColorProfile(Vec4(0.3, 0.6, 0.8, 0.6), Vec4(1.0, 1.0, 1.0, 1.0), Vec4(0.4, 0.8, 1.0, 1.0), Vec4(0.3, 0.6, 0.8, 0.6)), + ), + + # System messages + WhisperType.WTSystem: ( + ColorProfile(Vec4(0.0, 0.0, 0.0, 1.0), Vec4(1.0, 0.5, 0.5, 1.0), Vec4(0.0, 0.0, 0.0, 1.0), Vec4(0.0, 0.0, 0.0, 1.0)), + ColorProfile(Vec4(0.8, 0.3, 0.6, 0.6), Vec4(1.0, 1.0, 1.0, 1.0), Vec4(0.8, 0.4, 1.0, 1.0), Vec4(0.8, 0.3, 0.6, 0.6)), + ), + + # Battle SOS + WhisperType.WTBattleSOS: ( + ColorProfile(Vec4(0.0, 0.0, 0.0, 1.0), Vec4(1.0, 0.5, 0.5, 1.0), Vec4(0.0, 0.0, 0.0, 1.0), Vec4(0.0, 0.0, 0.0, 1.0)), + ColorProfile(Vec4(0.8, 0.3, 0.6, 0.6), Vec4(1.0, 1.0, 1.0, 1.0), Vec4(0.8, 0.4, 1.0, 1.0), Vec4(0.8, 0.3, 0.6, 0.6)), + ), + + # Emote whispers + WhisperType.WTEmote: ( + ColorProfile(Vec4(0.0, 0.0, 0.0, 1.0), Vec4(1.0, 0.5, 0.5, 1.0), Vec4(0.0, 0.0, 0.0, 1.0), Vec4(0.0, 0.0, 0.0, 1.0)), + ColorProfile(Vec4(0.3, 0.6, 0.8, 0.6), Vec4(1.0, 1.0, 1.0, 1.0), Vec4(0.4, 1.0, 1.0, 0.4), Vec4(0.3, 0.8, 0.3, 0.6)), + ), + + # Boarding groups + WhisperType.WTToontownBoardingGroup: ( + ColorProfile(Vec4(0.0, 0.0, 0.0, 1.0), Vec4(1.0, 0.5, 0.5, 1.0), Vec4(0.0, 0.0, 0.0, 1.0), Vec4(0.0, 0.0, 0.0, 1.0)), + ColorProfile(Vec4(0.97, 0.43, 0.1, 0.6), Vec4(1.0, 1.0, 1.0, 1.0), Vec4(0.98, 0.6, 0.38, 0.6), Vec4(0.97, 0.43, 0.1, 0.6)), + ), + + # Commands + WhisperType.WTMagicWord: ( + ColorProfile(Vec4(0.0, 0.0, 0.0, 1.0), Vec4(1.0, 0.5, 0.5, 1.0), Vec4(0.0, 0.0, 0.0, 1.0), Vec4(0.0, 0.0, 0.0, 1.0)), + ColorProfile(Vec4(0.7, 0.7, 0.1, 0.6), Vec4(1.0, 1.0, 0.4, 0.8), Vec4(0.8, 0.8, 0.2, 0.6), Vec4(0.8, 0.8, 0.1, 0.6)), + ), + +} + +# Indeces of what is stored in the above dict per whisper type +TEXT_COLOR = 0 +BACKGROUND_COLOR = 1 + + +# Given a whisper type, return its color profile for text. +# If not found, returns gray as a fallback. +def getWhisperTextColorProfile(color_code: WhisperType): + + colors = WHISPER_COLORS.get(color_code) + if colors is None: + print(f'WhisperGlobals.getWhisperTextColorProfile(): Unknown color code: {color_code}! Defaulting to GRAY.') + return GRAY + + return colors[TEXT_COLOR] + + +# Given a whisper type, +def getWhisperBackgroundColorProfile(color_code): + + colors = WHISPER_COLORS.get(color_code) + if colors is None: + print(f'WhisperGlobals.getWhisperBackgroundColorProfile(): Unknown color code: {color_code}! Defaulting to GRAY.') + return GRAY + + return colors[BACKGROUND_COLOR] + + + diff --git a/libotp/nametag/WhisperPopup.py b/libotp/nametag/WhisperPopup.py new file mode 100644 index 0000000..1269259 --- /dev/null +++ b/libotp/nametag/WhisperPopup.py @@ -0,0 +1,168 @@ +from typing import Union + +from .ColorProfile import ColorProfile +from . import WhisperGlobals +from .ClickablePopup import * +from .MarginPopup import * +from . import NametagGlobals + + +class WhisperPopup(ClickablePopup, MarginPopup): + + def __init__(self, text, font, type, bg_override: ColorProfile = None): + ClickablePopup.__init__(self) + MarginPopup.__init__(self) + + self.m_text = text + self.m_font = font + self.m_type = type + + self.m_np_balloon = None + self.m_avname = '' + self.m_region = None + self.m_mouse_watcher = None + self.m_manager = None + + # self.cbNode = CallbackNode(self.getName() + '-cbNode') + # self.cbNode.setCullCallback(PythonCallbackObject(self.cullCallback)) + # self.addChild(self.cbNode) + NametagGlobals._active_whisper_popups.add(self) + + self.m_time = 0 + self.m_culled = False + self.m_clickable = False + self.m_avid = 0 + self.m_is_player = False + self.m_is_player_id = None + self.m_state = 3 + self.m_objcode = 0 + + self.bg_override: Union[ColorProfile, None] = bg_override + + def setClickable(self, avatar_name, avatar_id, is_player_id=False): + self.m_clickable = True + self.m_avname = avatar_name + self.m_avid = avatar_id + self.m_is_player_id = is_player_id + self.m_state = 0 + + def click(self): + messenger.send('clickedWhisper', [self.m_avid, self.m_is_player]) + + def considerVisible(self): + if self.m_clickable and self.m_visible and self.m_mouse_watcher != NametagGlobals._mouse_watcher: + return False + + if self.m_seq != NametagGlobals._margin_prop_seq: + self.m_seq = NametagGlobals._margin_prop_seq + self.updateContents() + + return True + + def manage(self, manager): + self.m_manager = manager + manager.managePopup(self) + + def unmanage(self, manager): + manager.unmanagePopup(self) + del self.m_manager + + def cullCallback(self, *args): + if not self.m_culled: + self.m_culled = True + self.m_time = globalClock.getFrameTime() + + def setVisible(self, value): + MarginPopup.setVisible(self, value) + self.updateContents() + + if self.m_clickable: + if self.m_region: + if self.m_visible: + self.m_mouse_watcher = NametagGlobals._mouse_watcher + self.m_mouse_watcher.addRegion(self.m_region) + + elif self.m_mouse_watcher: + self.m_mouse_watcher.removeRegion(self.m_region) + self.m_mouse_watcher = None + + def setRegion(self, frame, sort): + if self.m_region: + self.m_region.setFrame(frame) + + else: + self.m_region = self._createRegion(frame) + + self.m_region.setSort(sort) + + def updateContents(self): + if self.m_np_balloon: + self.m_np_balloon.removeNode() + self.m_np_balloon = None + + if self.m_visible: + self.generateText(NametagGlobals._speech_balloon_2d, self.m_text, self.m_font) + + # Based on the whisper type, return a text color profile associated with it + def getTextColorProfile(self) -> ColorProfile: + return WhisperGlobals.getWhisperTextColorProfile(self.m_type).copy() + + # Based on the whisper type, return a background color profile associated with it + def getBackgroundColorProfile(self) -> ColorProfile: + + # Do we have an override? + if self.bg_override is not None: + return self.bg_override.copy() + + return WhisperGlobals.getWhisperBackgroundColorProfile(self.m_type).copy() + + def generateText(self, balloon, text, font): + text_color = self.getTextColorProfile().getColorFromState(self.m_state) + balloon_color = self.getBackgroundColorProfile().getColorFromState(self.m_state) + balloon_color[3] = max(balloon_color[3], NametagGlobals._min_2d_alpha) + balloon_color[3] = min(balloon_color[3], NametagGlobals._max_2d_alpha) + + balloon_result = balloon.generate(text, font, 8.0, text_color, balloon_color, + False, False, 0, None, False, False, None) + + self.m_np_balloon = self.m_np.attachNewNode(balloon_result) + + v34 = self.m_cell_width * 0.22222222 + v35 = balloon.m_text_height * balloon.m_hscale * 0.5 + v57 = -balloon.m_hscale * 5.5 + v16 = -(NametagGlobals._balloon_text_origin[2] + v35) + + v64 = Mat4(v34, 0, 0, 0, + 0, v34, 0, 0, + 0, 0, v34, 0, + v57 * v34, 0, v16 * v34, 1.0) + + self.m_np_balloon.setMat(v64) + + reducer = SceneGraphReducer() + reducer.applyAttribs(self.m_np_balloon.node()) + + if self.m_clickable: + v22 = self.m_np.getNetTransform().getMat() + v39, _, v41 = v22.xformPoint(Point3(v57 * v34, 0.0, v16 * v34)) + v27, _, v28 = v22.xformPoint(Point3(-v57 * v34, 0.0, -v16 * v34)) + self.setRegion(Vec4(v39, v27, v41, v28), 0) + + def setObjectCode(self, objcode): + self.m_objcode = objcode + + def getObjectCode(self): + return self.m_objcode + + def getScore(self): + result = 2000 + + if self.m_culled: + elapsed = globalClock.getFrameTime() - self.m_time + result -= elapsed * 200 + + # moved from considerManage: + if elapsed > 15.0: + self.unmanage(self.m_manager) + + return result diff --git a/libotp/nametag/__init__.py b/libotp/nametag/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libotp/nametag/_constants.py b/libotp/nametag/_constants.py new file mode 100644 index 0000000..543c462 --- /dev/null +++ b/libotp/nametag/_constants.py @@ -0,0 +1,13 @@ +CFSpeech = 1 +CFThought = 2 +CFQuicktalker = 4 +CFTimeout = 8 +CFPageButton = 16 +CFQuitButton = 32 +CFReversed = 64 +CFSndOpenchat = 128 +CFNoQuitButton = 256 + +# Some nametag modules expect PGButton to be in this namespace via +# `from ._constants import *`. +from direct.gui.DirectGuiGlobals import PGButton diff --git a/toontown/hood/OutdoorLighting.py b/toontown/hood/OutdoorLighting.py new file mode 100644 index 0000000..6e9e8f6 --- /dev/null +++ b/toontown/hood/OutdoorLighting.py @@ -0,0 +1,4949 @@ +"""Advanced cinematic outdoor lighting system for Toontown. + +Zone lighting profiles – each zone (and street variant) captures a real-world +atmospheric feel: + tt / tt_street – Toontown Central: warm Nebraska afternoon ~3 PM golden hour + dd / dd_street – Donald's Dock: heavy overcast maritime fog, diffuse daylight + dg / dg_street – Daisy's Gardens: blazing Virginia spring noon, intense blue sky + mm / mm_street – Minnie's Melodyland: deep California sunset, orange-purple sky + br / br_street – The Brrrgh: arctic blizzard, pale powder-blue light + dl / dl_street – Donald's Dreamland: moonlit midnight, cool blue-purple starry sky + gs – Goofy Speedway: hazy mid-morning race day, light dusty haze + sellbot_hq – dark purple factory smog + cashbot_hq – sickly green money trainyard + lawbot_hq – cold steel-blue overcast courtroom evening + bossbot_hq – near-pitch-black boardroom with brown ember undertones + factory_int – Sellbot / Lawbot factory interior: dim green-tinted machinery light + mint_int – Cashbot Mint interior: cold metallic pale-green vaults + office_int – Lawbot Office interior: harsh white corridor fluorescents + bossbot_cc – Bossbot Country Club interior: mahogany dimness + warm lamp fill + golf_course – Bossbot Golf Course exterior: verdant sunlit fairway + +New / upgraded systems +──────────────────────── +POST-PROCESS (no full-scene RTT compositor) + • CommonFilters bloom on the main window (adds glow on bright areas). + • Screen-space sun shafts via a lightweight render2dp overlay (sunrays shaders). + • Avoids the custom FilterManager “scene → texture → fullscreen quad” path that + triggered bottom-left viewport bugs on some drivers. + Controlled by lighting-bloom-enabled, lighting-god-rays, zone bloom/ray params. + +PROCEDURAL SKY + • ProceduralSky replaces hood.sky model with a shader-driven atmospheric sky + dome: Rayleigh + Mie scattering, FBM cumulus clouds that react to sunlight, + night-time star field, optional moon disc (Donald's Dreamland). + • Toggle: want-procedural-sky (default True). Falls back to model sky if + shaders unavailable. + +SHADOW SYSTEM + • Full-zone coverage: shadow film size computed from actual scene geometry. + • 2D card / billboard geometry is excluded from the shadow PASS (shadow + camera mask) so flat foliage planes don't cast false block shadows. They + still *receive* shadows normally. + • Transparent texture planes (foliage, building facades) receive and cast + shadows correctly via clearLightOff + a dedicated shadow mask BitMask32. + • Per-zone shadowDarknessFloor prevents pitch-black even in deep shadow. + +DAY / NIGHT CYCLE + • Per-zone keyframe tables; all parameters smoothly interpolated. + • All new parameters (cloud coverage, star brightness, Mie turbidity, etc.) + are also interpolated across the day/night cycle. + • Toggle: want-day-night-cycle. + +BACKWARD COMPATIBILITY + Public API unchanged: begin(), end(), shadeExtraSubtree(), + clearExtraSubtree(), refreshSettings(). + New: setTimeOfDay(), setupWaterReflection(), setWaterReflectionQuality(). +""" + +from __future__ import annotations + +import math +import os +import sys +from typing import Any + +from panda3d.core import ( + AmbientLight, + BitMask32, + BillboardEffect, + CardMaker, + ColorBlendAttrib, + ConfigVariableBool, + ConfigVariableString, + DepthOffsetAttrib, + DirectionalLight, + Filename, + Fog, + Geom, + GeomEnums, + GeomNode, + GeomPrimitive, + GeomTriangles, + GeomVertexArrayFormat, + GeomVertexData, + GeomVertexFormat, + GeomVertexReader, + GeomVertexWriter, + LightAttrib, + Material, + NodePath, + PlaneNode, + PointLight, + PandaSystem, + Quat, + RenderState, + Shader, + ShaderAttrib, + Texture, + TransparencyAttrib, + Vec3, + Vec4, +) + +try: + from otp.avatar import ShadowCaster as _OTPDropshadow +except Exception: + _OTPDropshadow = None + +from direct.showbase.ShowBaseGlobal import globalClock +from direct.task.TaskManagerGlobal import taskMgr +try: + # Optional: for debug hotkeys (ShowBase provides .accept) + from direct.showbase.DirectObject import DirectObject # type: ignore +except Exception: + DirectObject = None # type: ignore +try: + from otp.otpbase import OTPRender as _OTPRender # type: ignore +except Exception: + _OTPRender = None # type: ignore +from panda3d.core import ConfigVariableBool + +# `base` can be unavailable early in boot. Resolve safely. +try: + from direct.showbase.ShowBaseGlobal import base # type: ignore +except Exception: + base = None # type: ignore + +if base is None: + try: + import builtins + base = getattr(builtins, 'base', None) # type: ignore + except Exception: + base = None # type: ignore + + +def _syncBase() -> None: + """Refresh the module-global `base` reference. + + This module can be imported before ShowBase/ToonBase is constructed, in which + case `base` is None at import time. Later, callers wrap begin()/end() in + try/except (to keep loading robust), which can silently swallow AttributeError + and make it look like OutdoorLighting never ran. Keep `base` synced at runtime. + """ + global base + try: + if base is not None and getattr(base, 'render', None) is not None: + return + except Exception: + pass + try: + from direct.showbase import ShowBaseGlobal as _SBG # type: ignore + b = getattr(_SBG, 'base', None) + if b is not None: + base = b # type: ignore + return + except Exception: + pass + try: + import builtins + b = getattr(builtins, 'base', None) + if b is not None: + base = b # type: ignore + except Exception: + pass + + +def _getSettingValue(key: str, default): + """Fetch a setting from either Settings.getSetting(key, default) or Settings.get(key). + Falls back to Panda3D ConfigVariable if base.settings is missing or does not have the key. + """ + _syncBase() + settings = getattr(base, 'settings', None) + if settings is not None: + getter = getattr(settings, 'getSetting', None) or getattr(settings, 'get', None) + if getter is not None: + try: + # Try getting the setting directly first + v = getter(key) + if v is not None: + return v + except TypeError: + try: + v = getter(key, default) + if v is not None: + return v + except Exception: + pass + except Exception: + pass + + # Fallback to Panda3D ConfigVariable + if isinstance(default, bool): + return ConfigVariableBool(key, default).value + elif isinstance(default, (int, float)): + return ConfigVariableDouble(key, float(default)).value + else: + return ConfigVariableString(key, str(default)).value + + +def _debugEnabled() -> bool: + try: + v = _getSettingValue('lighting-debug', None) + if v is not None: + return bool(v) + except Exception: + pass + return ConfigVariableBool('lighting-debug', False).value + + +def _dbg(msg: str) -> None: + if not _debugEnabled(): + return + try: + print(f"[OutdoorLighting] {msg}") + except Exception: + pass + + +# Bisect defaults back to normal behavior: everything enabled. +_DEFAULT_BISECT_STEP = 2 # TEMP: bisect crash — testing shaderAuto + lights with sky setLightOff(101) fix +_BISECT_STEP = int(_DEFAULT_BISECT_STEP) +_DIR_LIGHT_MODE = 'all' # 'key' | 'key_fill' | 'all' + + +def setBisectStep(step: int) -> None: + """Set the runtime feature-bisect step (script-controlled). + + This intentionally does NOT read PRC/config, so it cannot be overridden by + external settings while we're isolating artifacts. + """ + global _BISECT_STEP + try: + _BISECT_STEP = max(0, int(step)) + except Exception: + _BISECT_STEP = 0 + + +def setDirectionalMode(mode: str) -> None: + """Control which directional lights are spawned (bisect within step 3/4). + + - 'key': key directional only + - 'key_fill': key + fill + - 'all': key + fill + rim (if profile has rim) + """ + global _DIR_LIGHT_MODE + m = (mode or '').strip().lower() + _DIR_LIGHT_MODE = m if m in ('key', 'key_fill', 'all') else 'all' + + +_HOTKEYS_TAG = 'osl_bisect_hotkeys_bound' +_HOTKEYS_ENABLED = False + + +def _maybeBindBisectHotkeys() -> None: + """Bind debug hotkeys to control bisect at runtime. + + This avoids needing any external injector. Only binds when lighting-debug is on. + """ + if (not _HOTKEYS_ENABLED) or (not _debugEnabled()): + return + _syncBase() + if base is None: + return + try: + if getattr(base, 'render', None) is None: + return + if base.render.getPythonTag(_HOTKEYS_TAG): + return + except Exception: + pass + + # ShowBase implements .accept. Bind on base itself. + try: + accept = getattr(base, 'accept', None) + if accept is None: + return + except Exception: + return + + def _set_step(s: int): + setBisectStep(s) + _dbg(f"hotkey: setBisectStep({s}) (re-enter zone to apply)") + + def _cycle_dir(): + global _DIR_LIGHT_MODE + order = ('key', 'key_fill', 'all') + try: + idx = order.index(_DIR_LIGHT_MODE) + except Exception: + idx = 2 + _DIR_LIGHT_MODE = order[(idx + 1) % len(order)] + _dbg(f"hotkey: setDirectionalMode('{_DIR_LIGHT_MODE}') (re-enter zone to apply)") + + try: + # Bisect steps 0-9 on number keys. + for s in range(10): + accept(str(s), _set_step, [s]) + # Directional mode cycle. + accept('f6', _cycle_dir) + accept('shift-f6', _cycle_dir) + try: + base.render.setPythonTag(_HOTKEYS_TAG, True) + except Exception: + pass + _dbg("bound bisect hotkeys: [0-9]=step, [F6]=cycle dir mode key->key_fill->all") + except Exception: + pass + + +def _bisectStep() -> int: + """Runtime subsystem bisect for isolating rendering artifacts. + + Default: `_DEFAULT_BISECT_STEP` (script-controlled). + + Enable order: + 0: light rig only + 1: + fog + sky tint + background clear color + 2: + procedural sky + 3: + postprocess bloom + 4: + godrays overlay + 5: + water shader/reflection + """ + try: + return int(_BISECT_STEP) + except Exception: + return int(_DEFAULT_BISECT_STEP) + + +def _bisectAllows(step: int) -> bool: + return _bisectStep() >= step + + +def _dbgBisectState() -> None: + if not _debugEnabled(): + return + s = _bisectStep() + _dbg( + "bisect " + f"step={s} " + f"shaderAuto={_bisectAllows(1)} " + f"lights={_bisectAllows(2)} " + f"sunNoShadows={_bisectAllows(3)} " + f"sunShadows={_bisectAllows(4)} " + f"fog_bg_tint={_bisectAllows(5)} " + f"proceduralSky={_bisectAllows(6)} " + f"bloom={_bisectAllows(7)} " + f"godrays={_bisectAllows(8)} " + f"water={_bisectAllows(9)}" + ) + + +def _spawnShadowTestScene() -> None: + """Spawn a simple cube+ground that should cast a visible shadow.""" + if not _debugEnabled(): + return + # The shadow test scene is useful while developing shadow-map logic, but + # should never appear in normal gameplay even if lighting-debug is enabled. + # Enable explicitly via PRC or settings. + try: + if not ConfigVariableBool('lighting-shadow-test-scene', False).value: + return + except Exception: + return + _syncBase() + try: + if base is None or getattr(base, 'render', None) is None: + return + except Exception: + return + try: + if base.render.getPythonTag('osl_shadow_test_spawned'): + return + except Exception: + pass + + try: + root = base.render.attachNewNode('OutdoorLightingShadowTest') + try: + root.wrtReparentTo(base.camera) + except Exception: + root.reparentTo(base.camera) + root.setPos(0, 25, -2) + + cm = CardMaker('shadowTestGround') + cm.setFrame(-10, 10, -10, 10) + ground = root.attachNewNode(cm.generate()) + ground.setP(-90) + ground.setZ(0) + ground.setColor(0.6, 0.6, 0.6, 1) + ground.setShaderAuto() + + # Procedural cube (no external model dependency). + try: + fmt = GeomVertexFormat.getV3n3() + vdata = GeomVertexData('shadowTestCube', fmt, Geom.UHStatic) + vwriter = GeomVertexWriter(vdata, 'vertex') + nwriter = GeomVertexWriter(vdata, 'normal') + + # Build a cube with 6 faces (24 verts so normals are per-face). + # Cube centered at origin, size 2. + faces = [ + (Vec3(0, 0, 1), [Vec3(-1, -1, 1), Vec3(1, -1, 1), Vec3(1, 1, 1), Vec3(-1, 1, 1)]), # top + (Vec3(0, 0, -1), [Vec3(-1, 1, -1), Vec3(1, 1, -1), Vec3(1, -1, -1), Vec3(-1, -1, -1)]), # bottom + (Vec3(0, 1, 0), [Vec3(-1, 1, 1), Vec3(1, 1, 1), Vec3(1, 1, -1), Vec3(-1, 1, -1)]), # front + (Vec3(0, -1, 0), [Vec3(1, -1, 1), Vec3(-1, -1, 1), Vec3(-1, -1, -1), Vec3(1, -1, -1)]), # back + (Vec3(1, 0, 0), [Vec3(1, 1, 1), Vec3(1, -1, 1), Vec3(1, -1, -1), Vec3(1, 1, -1)]), # right + (Vec3(-1, 0, 0), [Vec3(-1, -1, 1), Vec3(-1, 1, 1), Vec3(-1, 1, -1), Vec3(-1, -1, -1)]), # left + ] + + tris = GeomTriangles(Geom.UHStatic) + vidx = 0 + for nrm, verts in faces: + for v in verts: + vwriter.addData3(v) + nwriter.addData3(nrm) + # two triangles per quad (0,1,2) and (0,2,3) + tris.addVertices(vidx + 0, vidx + 1, vidx + 2) + tris.addVertices(vidx + 0, vidx + 2, vidx + 3) + vidx += 4 + + geom = Geom(vdata) + geom.addPrimitive(tris) + gnode = GeomNode('shadowTestCubeGeom') + gnode.addGeom(geom) + cube = root.attachNewNode(gnode) + cube.setPos(0, 0, 2.5) + cube.setScale(2) + cube.setColor(1, 0.2, 0.2, 1) + cube.setShaderAuto() + except Exception as e: + _dbg(f"shadow test cube build failed: {e!r}") + + base.render.setShaderAuto() + base.render.setPythonTag('osl_shadow_test_spawned', True) + _dbg("spawned shadow test scene near camera") + except Exception as e: + _dbg(f"shadow test spawn failed: {e!r}") + + +_NORMALS_TAG = 'osl_normals_generated' + + +def preprocessGeometry(root: NodePath) -> None: + """Prepare geometry before flattening. + + Sets setLightOff(101) on foliage/trees so they are not merged with lit + geometry during flattenMedium, preserving their unlit status. + """ + if root is None or root.isEmpty(): + return + try: + nodes = root.findAllMatches('**') + for i in range(nodes.getNumPaths()): + np = nodes.getPath(i) + if _isFoliageOrTree(np): + try: + np.setLightOff(101) + except Exception: + pass + except Exception: + pass + + +def _ensureNormals(root: NodePath) -> None: + """Generate vertex normals for geometry that has none or has zeroed normals. + + Some zone meshes in this project ship without a 'normal' column or have zeroed + normals after flattening, which makes all lighting/shadows appear flat or pitch black. + This generates/recomputes normals for those geoms. + """ + if root is None or root.isEmpty(): + return + try: + if root.getPythonTag(_NORMALS_TAG): + return + except Exception: + pass + + nodes = root.findAllMatches('**/+GeomNode') + if nodes.getNumPaths() <= 0: + return + + _dbg(f"Scanning and generating normals for {nodes.getNumPaths()} GeomNodes...") + + def _formatWithNormals(old_fmt: GeomVertexFormat) -> GeomVertexFormat: + # Copy all arrays/columns and append a normal column to the first array. + nf = GeomVertexFormat() + added = False + for ai in range(old_fmt.getNumArrays()): + old_arr = old_fmt.getArray(ai) + arr = GeomVertexArrayFormat() + for ci in range(old_arr.getNumColumns()): + col = old_arr.getColumn(ci) + arr.addColumn(col.getName(), col.getNumComponents(), + col.getNumericType(), col.getContents()) + if not added: + arr.addColumn('normal', 3, GeomEnums.NT_float32, GeomEnums.C_normal) + added = True + nf.addArray(arr) + return GeomVertexFormat.registerFormat(nf) + + def _vdataWithFormat(old_vdata: GeomVertexData, new_fmt: GeomVertexFormat) -> GeomVertexData | None: + try: + if hasattr(old_vdata, 'convertTo'): + return old_vdata.convertTo(new_fmt) + except Exception: + pass + + try: + num_rows = old_vdata.getNumRows() + except Exception: + return None + + try: + new_vdata = GeomVertexData(old_vdata) + new_vdata.setFormat(new_fmt) + new_vdata.setNumRows(num_rows) + except Exception: + try: + new_vdata = GeomVertexData('with_normals', new_fmt, old_vdata.getUsageHint()) + new_vdata.setNumRows(num_rows) + except Exception: + return None + + try: + old_fmt = old_vdata.getFormat() + for ai in range(old_fmt.getNumArrays()): + arr = old_fmt.getArray(ai) + for ci in range(arr.getNumColumns()): + col = arr.getColumn(ci) + name = col.getName() + if name == 'normal': + continue + try: + if not old_vdata.hasColumn(name) or not new_vdata.hasColumn(name): + continue + except Exception: + continue + + r = GeomVertexReader(old_vdata, name) + w = GeomVertexWriter(new_vdata, name) + comps = int(col.getNumComponents()) + for row in range(num_rows): + r.setRow(row) + w.setRow(row) + if comps == 1: + w.setData1(r.getData1()) + elif comps == 2: + w.setData2(r.getData2()) + elif comps == 3: + w.setData3(r.getData3()) + else: + w.setData4(r.getData4()) + except Exception: + pass + + return new_vdata + + # Iterate each Geom and compute normals. + for ni in range(nodes.getNumPaths()): + np = nodes.getPath(ni) + gnode = np.node() + if not isinstance(gnode, GeomNode): + continue + for gi in range(gnode.getNumGeoms()): + try: + geom = gnode.modifyGeom(gi) + except Exception: + continue + if not isinstance(geom, Geom): + continue + try: + vdata = geom.modifyVertexData() + except Exception: + continue + if vdata is None: + continue + + try: + if vdata.hasColumn('normal'): + continue + except Exception: + continue + + try: + new_fmt = _formatWithNormals(vdata.getFormat()) + new_vdata = _vdataWithFormat(vdata, new_fmt) + if new_vdata is None: + continue + geom.setVertexData(new_vdata) + vdata = geom.modifyVertexData() + except Exception: + continue + + try: + num_rows = vdata.getNumRows() + except Exception: + continue + if num_rows <= 0: + continue + + # Accumulate normals in Python list. + acc = [Vec3(0, 0, 0) for _ in range(num_rows)] + vr = GeomVertexReader(vdata, 'vertex') + + def _getPos(row: int) -> Vec3: + vr.setRow(row) + return vr.getData3() + + try: + for pi in range(geom.getNumPrimitives()): + prim = geom.getPrimitive(pi) + if prim is None: + continue + try: + decomp = prim.decompose() + except Exception: + decomp = prim + try: + decomp = decomp.decompose() + except Exception: + pass + try: + npr = decomp.getNumPrimitives() + except Exception: + npr = 0 + for t in range(npr): + start = decomp.getPrimitiveStart(t) + end = decomp.getPrimitiveEnd(t) + if end - start < 3: + continue + try: + i0 = decomp.getVertex(start + 0) + i1 = decomp.getVertex(start + 1) + i2 = decomp.getVertex(start + 2) + except Exception: + continue + if i0 >= num_rows or i1 >= num_rows or i2 >= num_rows: + continue + p0 = _getPos(i0) + p1 = _getPos(i1) + p2 = _getPos(i2) + n = (p1 - p0).cross(p2 - p0) + if n.lengthSquared() <= 1e-12: + continue + acc[i0] += n + acc[i1] += n + acc[i2] += n + except Exception: + continue + + vw = GeomVertexWriter(vdata, 'normal') + try: + for r in range(num_rows): + n = acc[r] + if n.lengthSquared() <= 1e-12: + n = Vec3(0, 0, 1) + else: + n.normalize() + vw.setRow(r) + vw.setData3(n) + except Exception: + pass + + try: + root.setPythonTag(_NORMALS_TAG, True) + except Exception: + pass + + +def _forceShaderRegen(np: NodePath) -> None: + """Force shader generator to rebuild shaders after vertex format changes.""" + if np is None or np.isEmpty(): + return + try: + np.clearShader() + except Exception: + pass + try: + np.setShaderAuto() + except Exception: + pass + + +def _forceShadersOnSubtree(root: NodePath) -> None: + """Clear shader/shader-off attribs that can block auto-shaders (and shadows).""" + if root is None or root.isEmpty(): + return + try: + nodes = root.findAllMatches('**') + for i in range(nodes.getNumPaths()): + np = nodes.getPath(i) + try: + # Clear explicit shader attribs (including shader-off). + try: + np.clearAttrib(ShaderAttrib.getClassType()) + except Exception: + pass + # Clear any fixed shader and re-enable auto. + try: + np.clearShader() + except Exception: + pass + try: + np.setShaderAuto() + except Exception: + pass + except Exception: + pass + except Exception: + pass + +# Shader bisect — increment to add the next custom GLSL subsystem: +# 0 = modern outdoor stack off (begin() no-ops; legacy model sky in SkyUtil). +# 1 = procedural sky GLSL only (light rig + fog/tint/bg + sky dome; no HDR post, +# bloom, god rays, or water GLSL). +# 2 = + post-process (see _LEVEL2_POST_SINGLE; one subsystem at a time for debugging). +# 3 = + water reflection GLSL (full pipeline). +_OUTDOOR_SHADER_BISECT_LEVEL = 3 + +# When bisect level is 2, enable exactly one post FX: +# 'full' / 'stack' — bloom + sun-ray overlay (default cinematic post). +# 'bloom' — CommonFilters bloom only. +# 'godrays' — sunrays card on render2dp only. +# Level 3+ always uses full post (bloom + god rays). +_LEVEL2_POST_SINGLE = 'full' + + +def _fixPostProcessRttViewports() -> None: + """Normalize every DisplayRegion on FilterManager offscreen buffers. + + Stock FilterManager.renderSceneInto uses ``buffer.makeDisplayRegion()`` with no + bounds; on some GL drivers that yields a sub-rectangle (often bottom-left + quarter). The scene is then rendered into only part of the colour texture, + while the compositing quad still samples the full texture — exactly the + broken layout players see. Forcing (0,1)×(0,1) on those regions fixes it. + + NOTE: _bloomFilters is a CommonFilters instance, NOT a FilterManager. + CommonFilters stores its FilterManager at _bloomFilters.manager — so + buffers live at _bloomFilters.manager.buffers, not _bloomFilters.buffers. + Getting this wrong means the bloom RTT buffers are never repaired. + """ + # Collect FilterManager instances that own offscreen buffers (bloom, etc.). + fms_to_fix = [] + if _bloomFilters is not None: + # _bloomFilters is a CommonFilters whose internal FilterManager is at .manager + inner = getattr(_bloomFilters, 'manager', None) + if inner is not None: + fms_to_fix.append(inner) + + for fm in fms_to_fix: + try: + for buf in getattr(fm, 'buffers', None) or []: + if buf is None: + continue + n = buf.getNumDisplayRegions() + for i in range(n): + dr = buf.getDisplayRegion(i) + if not dr: + continue + dr.setDimensions(0.0, 1.0, 0.0, 1.0) + if dr.supportsPixelZoom(): + dr.setPixelZoom(1) + try: + dr.setScissorEnabled(False) + except Exception: + pass + except Exception: + pass + +# ───────────────────────────────────────────────────────────────────────────── +# Zone lighting profiles +# ───────────────────────────────────────────────────────────────────────────── +_ZONE_PROFILES: dict[str, dict] = { + + # ── Toontown Central playground ─────────────────────────────────────────── + 'tt': { + 'ambient': (0.32, 0.38, 0.54, 1.0), + 'key': (1.45, 1.28, 0.96, 1.0), + 'keyHpr': (135, -42, 0), + 'fill': (0.32, 0.42, 0.62, 1.0), + 'fillHpr': (-45, -18, 0), + 'rim': (0.18, 0.24, 0.40, 1.0), + 'rimHpr': (315, -30, 0), + 'shadowCaster': True, + 'shadowRes': 4096, + 'shadowArea': 160, + 'shadowResScale': 0.75, + 'shadowAreaScale': 1.40, + 'shadowFollowDist': 280.0, + 'shadowDarknessFloor': 0.22, + 'fogColor': (0.72, 0.80, 0.92, 1.0), + 'fogNear': 110.0, + 'fogFar': 560.0, + 'fogExponent': None, + 'skyScale': (1.04, 1.01, 0.91, 1.0), + 'clearColor': (0.50, 0.68, 0.90, 1.0), + 'sunUV': (0.65, 0.78), + 'rayColor': (1.00, 0.94, 0.68, 1.0), + 'rayIntensity': 0.48, + 'bloomIntensity': 0.40, + 'bloomThreshold': 0.56, + 'exposure': 1.00, + 'hasWater': True, + 'waterColor': (0.28, 0.52, 0.72, 0.88), + 'waterReflQuality': 'medium', + 'dayNightEnabled': True, + 'cloudCoverage': 0.42, + 'cloudSpeed': 0.70, + 'cloudSharpness': 0.55, + 'turbidity': 2.5, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── TT streets ──────────────────────────────────────────────────────────── + 'tt_street': { + 'ambient': (0.28, 0.34, 0.48, 1.0), + 'key': (1.38, 1.22, 0.90, 1.0), + 'keyHpr': (140, -40, 0), + 'fill': (0.30, 0.40, 0.58, 1.0), + 'fillHpr': (-50, -20, 0), + 'rim': (0.15, 0.22, 0.38, 1.0), + 'rimHpr': (310, -28, 0), + 'shadowCaster': True, + 'shadowRes': 2048, + 'shadowArea': 140, + 'shadowResScale': 1.00, + 'shadowAreaScale': 1.20, + 'shadowFollowDist': 220.0, + 'shadowDarknessFloor': 0.20, + 'fogColor': (0.70, 0.78, 0.90, 1.0), + 'fogNear': 95.0, + 'fogFar': 480.0, + 'fogExponent': None, + 'skyScale': (1.04, 1.01, 0.91, 1.0), + 'clearColor': (0.48, 0.66, 0.88, 1.0), + 'sunUV': (0.65, 0.78), + 'rayColor': (1.00, 0.92, 0.66, 1.0), + 'rayIntensity': 0.38, + 'bloomIntensity': 0.36, + 'bloomThreshold': 0.58, + 'exposure': 1.00, + 'hasWater': False, + 'dayNightEnabled': True, + 'cloudCoverage': 0.40, + 'cloudSpeed': 0.68, + 'cloudSharpness': 0.55, + 'turbidity': 2.4, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Donald's Dock playground ────────────────────────────────────────────── + 'dd': { + 'ambient': (0.60, 0.63, 0.68, 1.0), + 'key': (0.70, 0.74, 0.80, 1.0), + 'keyHpr': (0, -80, 0), + 'fill': (0.52, 0.56, 0.62, 1.0), + 'fillHpr': (180, -55, 0), + 'rim': None, + 'shadowCaster': False, + 'shadowRes': 512, + 'shadowArea': 180, + 'shadowDarknessFloor': 0.22, + 'fogColor': (0.72, 0.76, 0.82, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.011, + 'skyScale': (0.84, 0.87, 0.92, 1.0), + 'clearColor': (0.70, 0.74, 0.80, 1.0), + 'sunUV': (0.50, 0.88), + 'rayColor': (0.90, 0.93, 0.98, 1.0), + 'rayIntensity': 0.00, + 'bloomIntensity': 0.18, + 'bloomThreshold': 0.68, + 'exposure': 0.95, + 'hasWater': True, + 'waterColor': (0.22, 0.32, 0.44, 0.90), + 'waterReflQuality': 'high', + 'dayNightEnabled': True, + 'cloudCoverage': 0.88, + 'cloudSpeed': 1.10, + 'cloudSharpness': 0.20, + 'turbidity': 5.5, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── DD streets ──────────────────────────────────────────────────────────── + 'dd_street': { + 'ambient': (0.58, 0.61, 0.66, 1.0), + 'key': (0.68, 0.72, 0.78, 1.0), + 'keyHpr': (0, -78, 0), + 'fill': (0.50, 0.54, 0.60, 1.0), + 'fillHpr': (180, -52, 0), + 'rim': None, + 'shadowCaster': False, + 'shadowRes': 512, + 'shadowArea': 140, + 'shadowDarknessFloor': 0.20, + 'fogColor': (0.70, 0.74, 0.80, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.014, + 'skyScale': (0.84, 0.87, 0.92, 1.0), + 'clearColor': (0.68, 0.72, 0.78, 1.0), + 'sunUV': (0.50, 0.85), + 'rayColor': (0.88, 0.92, 0.96, 1.0), + 'rayIntensity': 0.00, + 'bloomIntensity': 0.15, + 'bloomThreshold': 0.70, + 'exposure': 0.93, + 'hasWater': False, + 'dayNightEnabled': True, + 'cloudCoverage': 0.90, + 'cloudSpeed': 1.20, + 'cloudSharpness': 0.18, + 'turbidity': 5.8, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Daisy's Gardens playground ──────────────────────────────────────────── + 'dg': { + 'ambient': (0.28, 0.36, 0.56, 1.0), + 'key': (1.48, 1.36, 1.10, 1.0), + 'keyHpr': (170, -68, 0), + 'fill': (0.34, 0.46, 0.68, 1.0), + 'fillHpr': (-10, -22, 0), + 'rim': (0.18, 0.28, 0.48, 1.0), + 'rimHpr': (0, 55, 0), + 'shadowCaster': True, + 'shadowRes': 4096, + 'shadowArea': 155, + 'shadowResScale': 1.00, + 'shadowAreaScale': 0.95, + 'shadowFollowDist': 260.0, + 'shadowDarknessFloor': 0.22, + 'fogColor': (0.62, 0.80, 1.00, 1.0), + 'fogNear': 145.0, + 'fogFar': 660.0, + 'fogExponent': None, + 'skyScale': (0.88, 0.96, 1.14, 1.0), + 'clearColor': (0.36, 0.62, 1.00, 1.0), + 'sunUV': (0.54, 0.90), + 'rayColor': (1.00, 1.00, 0.88, 1.0), + 'rayIntensity': 0.68, + 'bloomIntensity': 0.52, + 'bloomThreshold': 0.52, + 'exposure': 1.05, + 'hasWater': True, + 'waterColor': (0.24, 0.54, 0.36, 0.84), + 'waterReflQuality': 'medium', + 'dayNightEnabled': True, + 'cloudCoverage': 0.28, + 'cloudSpeed': 0.55, + 'cloudSharpness': 0.70, + 'turbidity': 1.8, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── DG streets ──────────────────────────────────────────────────────────── + 'dg_street': { + 'ambient': (0.24, 0.32, 0.50, 1.0), + 'key': (1.40, 1.28, 1.04, 1.0), + 'keyHpr': (172, -66, 0), + 'fill': (0.30, 0.42, 0.62, 1.0), + 'fillHpr': (-12, -20, 0), + 'rim': (0.15, 0.24, 0.42, 1.0), + 'rimHpr': (0, 52, 0), + 'shadowCaster': True, + 'shadowRes': 2048, + 'shadowArea': 130, + 'shadowResScale': 1.00, + 'shadowAreaScale': 1.10, + 'shadowFollowDist': 220.0, + 'shadowDarknessFloor': 0.20, + 'fogColor': (0.60, 0.78, 0.98, 1.0), + 'fogNear': 125.0, + 'fogFar': 580.0, + 'fogExponent': None, + 'skyScale': (0.88, 0.96, 1.14, 1.0), + 'clearColor': (0.34, 0.60, 0.98, 1.0), + 'sunUV': (0.54, 0.88), + 'rayColor': (1.00, 1.00, 0.88, 1.0), + 'rayIntensity': 0.60, + 'bloomIntensity': 0.46, + 'bloomThreshold': 0.55, + 'exposure': 1.05, + 'hasWater': False, + 'dayNightEnabled': True, + 'cloudCoverage': 0.24, + 'cloudSpeed': 0.52, + 'cloudSharpness': 0.72, + 'turbidity': 1.8, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Minnie's Melodyland playground ─────────────────────────────────────── + 'mm': { + 'ambient': (0.42, 0.28, 0.30, 1.0), + 'key': (1.10, 0.50, 0.18, 1.0), + 'keyHpr': (260, -14, 0), + 'fill': (0.22, 0.18, 0.40, 1.0), + 'fillHpr': (80, -28, 0), + 'rim': (0.90, 0.40, 0.12, 1.0), + 'rimHpr': (258, -8, 0), + 'shadowCaster': True, + 'shadowRes': 2048, + 'shadowArea': 200, + 'shadowResScale': 0.85, + 'shadowAreaScale': 1.55, + 'shadowFollowDist': 260.0, + 'shadowDarknessFloor': 0.20, + 'fogColor': (0.80, 0.44, 0.20, 1.0), + 'fogNear': 10.0, + 'fogFar': 330.0, + 'fogExponent': None, + 'skyScale': (1.30, 0.72, 0.48, 1.0), + 'clearColor': (0.88, 0.46, 0.18, 1.0), + 'sunUV': (0.10, 0.43), + 'rayColor': (1.00, 0.58, 0.22, 1.0), + 'rayIntensity': 0.70, + 'bloomIntensity': 0.55, + 'bloomThreshold': 0.55, + 'exposure': 1.02, + 'hasWater': False, + 'dayNightEnabled': True, + 'cloudCoverage': 0.55, + 'cloudSpeed': 0.85, + 'cloudSharpness': 0.45, + 'turbidity': 3.5, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── MM streets ──────────────────────────────────────────────────────────── + 'mm_street': { + 'ambient': (0.40, 0.26, 0.28, 1.0), + 'key': (1.06, 0.48, 0.16, 1.0), + 'keyHpr': (258, -12, 0), + 'fill': (0.20, 0.16, 0.38, 1.0), + 'fillHpr': (78, -26, 0), + 'rim': (0.86, 0.38, 0.10, 1.0), + 'rimHpr': (256, -6, 0), + 'shadowCaster': True, + 'shadowRes': 2048, + 'shadowArea': 160, + 'shadowResScale': 0.85, + 'shadowAreaScale': 1.40, + 'shadowFollowDist': 220.0, + 'shadowDarknessFloor': 0.18, + 'fogColor': (0.78, 0.42, 0.18, 1.0), + 'fogNear': 8.0, + 'fogFar': 280.0, + 'fogExponent': None, + 'skyScale': (1.30, 0.72, 0.48, 1.0), + 'clearColor': (0.86, 0.44, 0.16, 1.0), + 'sunUV': (0.10, 0.42), + 'rayColor': (1.00, 0.56, 0.20, 1.0), + 'rayIntensity': 0.62, + 'bloomIntensity': 0.50, + 'bloomThreshold': 0.56, + 'exposure': 1.02, + 'hasWater': False, + 'dayNightEnabled': True, + 'cloudCoverage': 0.52, + 'cloudSpeed': 0.88, + 'cloudSharpness': 0.42, + 'turbidity': 3.6, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── The Brrrgh playground ───────────────────────────────────────────────── + 'br': { + 'ambient': (0.42, 0.50, 0.68, 1.0), + 'key': (0.62, 0.72, 0.90, 1.0), + 'keyHpr': (180, -18, 0), + 'fill': (0.38, 0.46, 0.62, 1.0), + 'fillHpr': (0, -12, 0), + 'rim': (0.48, 0.56, 0.74, 1.0), + 'rimHpr': (178, -6, 0), + 'shadowCaster': False, + 'shadowRes': 512, + 'shadowArea': 180, + 'shadowDarknessFloor': 0.24, + 'fogColor': (0.68, 0.78, 0.94, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.016, + 'skyScale': (0.78, 0.88, 1.08, 1.0), + 'clearColor': (0.62, 0.74, 0.92, 1.0), + 'sunUV': (0.50, 0.44), + 'rayColor': (0.82, 0.90, 1.00, 1.0), + 'rayIntensity': 0.08, + 'bloomIntensity': 0.20, + 'bloomThreshold': 0.70, + 'exposure': 0.90, + 'hasWater': False, + 'dayNightEnabled': True, + 'cloudCoverage': 0.78, + 'cloudSpeed': 1.40, + 'cloudSharpness': 0.15, + 'turbidity': 6.0, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── BR streets ──────────────────────────────────────────────────────────── + 'br_street': { + 'ambient': (0.40, 0.48, 0.66, 1.0), + 'key': (0.60, 0.70, 0.88, 1.0), + 'keyHpr': (180, -16, 0), + 'fill': (0.36, 0.44, 0.60, 1.0), + 'fillHpr': (0, -10, 0), + 'rim': (0.46, 0.54, 0.72, 1.0), + 'rimHpr': (178, -4, 0), + 'shadowCaster': False, + 'shadowRes': 512, + 'shadowArea': 140, + 'shadowDarknessFloor': 0.22, + 'fogColor': (0.66, 0.76, 0.92, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.020, + 'skyScale': (0.78, 0.88, 1.08, 1.0), + 'clearColor': (0.60, 0.72, 0.90, 1.0), + 'sunUV': (0.50, 0.42), + 'rayColor': (0.80, 0.88, 1.00, 1.0), + 'rayIntensity': 0.06, + 'bloomIntensity': 0.16, + 'bloomThreshold': 0.72, + 'exposure': 0.88, + 'hasWater': False, + 'dayNightEnabled': True, + 'cloudCoverage': 0.80, + 'cloudSpeed': 1.50, + 'cloudSharpness': 0.12, + 'turbidity': 6.2, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Donald's Dreamland playground ───────────────────────────────────────── + 'dl': { + 'ambient': (0.11, 0.15, 0.32, 1.0), + 'key': (0.58, 0.65, 0.92, 1.0), + 'keyHpr': (225, -55, 0), + 'fill': (0.07, 0.10, 0.24, 1.0), + 'fillHpr': (45, -15, 0), + 'rim': (0.38, 0.46, 0.72, 1.0), + 'rimHpr': (220, -50, 0), + 'shadowCaster': True, + 'shadowRes': 2048, + 'shadowArea': 170, + 'shadowResScale': 0.75, + 'shadowAreaScale': 1.45, + 'shadowFollowDist': 260.0, + 'shadowDarknessFloor': 0.14, + 'fogColor': (0.07, 0.10, 0.22, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.004, + 'skyScale': (0.42, 0.48, 0.74, 1.0), + 'clearColor': (0.04, 0.06, 0.14, 1.0), + 'sunUV': (0.30, 0.82), + 'rayColor': (0.58, 0.70, 1.00, 1.0), + 'rayIntensity': 0.42, + 'bloomIntensity': 0.32, + 'bloomThreshold': 0.62, + 'exposure': 1.30, + 'hasWater': False, + 'dayNightEnabled': True, + 'cloudCoverage': 0.22, + 'cloudSpeed': 0.25, + 'cloudSharpness': 0.50, + 'turbidity': 1.5, + 'starBrightness': 0.96, + 'moonEnabled': True, + 'moonDir': (225, -55, 0), + }, + + # ── DL streets ──────────────────────────────────────────────────────────── + 'dl_street': { + 'ambient': (0.09, 0.13, 0.28, 1.0), + 'key': (0.54, 0.62, 0.86, 1.0), + 'keyHpr': (225, -52, 0), + 'fill': (0.06, 0.09, 0.20, 1.0), + 'fillHpr': (42, -14, 0), + 'rim': (0.35, 0.44, 0.68, 1.0), + 'rimHpr': (222, -48, 0), + 'shadowCaster': True, + 'shadowRes': 2048, + 'shadowArea': 140, + 'shadowResScale': 0.75, + 'shadowAreaScale': 1.30, + 'shadowFollowDist': 220.0, + 'shadowDarknessFloor': 0.12, + 'fogColor': (0.06, 0.09, 0.20, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.005, + 'skyScale': (0.42, 0.48, 0.74, 1.0), + 'clearColor': (0.04, 0.06, 0.14, 1.0), + 'sunUV': (0.30, 0.80), + 'rayColor': (0.56, 0.68, 1.00, 1.0), + 'rayIntensity': 0.36, + 'bloomIntensity': 0.28, + 'bloomThreshold': 0.65, + 'exposure': 1.32, + 'hasWater': False, + 'dayNightEnabled': True, + 'cloudCoverage': 0.20, + 'cloudSpeed': 0.22, + 'cloudSharpness': 0.48, + 'turbidity': 1.5, + 'starBrightness': 0.92, + 'moonEnabled': True, + 'moonDir': (225, -55, 0), + }, + + # ── Goofy Speedway ──────────────────────────────────────────────────────── + 'gs': { + 'ambient': (0.30, 0.32, 0.40, 1.0), + 'key': (1.10, 1.00, 0.82, 1.0), + 'keyHpr': (130, -48, 0), + 'fill': (0.24, 0.28, 0.40, 1.0), + 'fillHpr': (-50, -30, 0), + 'rim': None, + 'shadowCaster': True, + 'shadowRes': 2048, + 'shadowArea': 180, + 'shadowResScale': 0.90, + 'shadowAreaScale': 1.20, + 'shadowFollowDist': 240.0, + 'shadowDarknessFloor': 0.20, + 'fogColor': (0.78, 0.76, 0.68, 1.0), + 'fogNear': 60.0, + 'fogFar': 380.0, + 'fogExponent': None, + 'skyScale': (1.06, 1.02, 0.84, 1.0), + 'clearColor': (0.56, 0.64, 0.72, 1.0), + 'sunUV': (0.60, 0.74), + 'rayColor': (1.00, 0.92, 0.70, 1.0), + 'rayIntensity': 0.26, + 'bloomIntensity': 0.24, + 'bloomThreshold': 0.64, + 'exposure': 1.00, + 'hasWater': False, + 'dayNightEnabled': True, + 'cloudCoverage': 0.32, + 'cloudSpeed': 0.90, + 'cloudSharpness': 0.50, + 'turbidity': 3.0, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Golf Course (Bossbot) exterior ──────────────────────────────────────── + 'golf_course': { + 'ambient': (0.24, 0.32, 0.28, 1.0), + 'key': (1.20, 1.12, 0.88, 1.0), + 'keyHpr': (160, -58, 0), + 'fill': (0.20, 0.30, 0.22, 1.0), + 'fillHpr': (-20, -28, 0), + 'rim': (0.12, 0.18, 0.10, 1.0), + 'rimHpr': (0, 48, 0), + 'shadowCaster': True, + 'shadowRes': 2048, + 'shadowArea': 200, + 'shadowResScale': 1.00, + 'shadowAreaScale': 1.30, + 'shadowFollowDist': 280.0, + 'shadowDarknessFloor': 0.18, + 'fogColor': (0.60, 0.78, 0.60, 1.0), + 'fogNear': 100.0, + 'fogFar': 520.0, + 'fogExponent': None, + 'skyScale': (0.88, 1.00, 0.82, 1.0), + 'clearColor': (0.40, 0.68, 0.44, 1.0), + 'sunUV': (0.56, 0.88), + 'rayColor': (1.00, 1.00, 0.80, 1.0), + 'rayIntensity': 0.50, + 'bloomIntensity': 0.38, + 'bloomThreshold': 0.60, + 'exposure': 1.05, + 'hasWater': True, + 'waterColor': (0.28, 0.60, 0.40, 0.82), + 'waterReflQuality': 'medium', + 'dayNightEnabled': False, + 'cloudCoverage': 0.30, + 'cloudSpeed': 0.65, + 'cloudSharpness': 0.60, + 'turbidity': 2.2, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Sellbot HQ (exterior) ───────────────────────────────────────────────── + 'sellbot_hq': { + 'ambient': (0.08, 0.05, 0.14, 1.0), + 'key': (0.22, 0.15, 0.34, 1.0), + 'keyHpr': (135, -65, 0), + 'fill': (0.06, 0.04, 0.10, 1.0), + 'fillHpr': (-60, -30, 0), + 'rim': (0.14, 0.08, 0.22, 1.0), + 'rimHpr': (130, -60, 0), + 'shadowCaster': False, + 'shadowRes': 512, + 'shadowArea': 180, + 'shadowDarknessFloor': 0.06, + 'fogColor': (0.12, 0.08, 0.18, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.020, + 'skyScale': (0.58, 0.38, 0.80, 1.0), + 'clearColor': (0.08, 0.05, 0.14, 1.0), + 'sunUV': (0.50, 0.50), + 'rayColor': (0.60, 0.38, 0.92, 1.0), + 'rayIntensity': 0.04, + 'bloomIntensity': 0.06, + 'bloomThreshold': 0.82, + 'exposure': 1.30, + 'hasWater': False, + 'dayNightEnabled': False, + 'cloudCoverage': 0.95, + 'cloudSpeed': 0.30, + 'cloudSharpness': 0.05, + 'turbidity': 8.0, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Sellbot Factory interior ────────────────────────────────────────────── + 'factory_int': { + 'ambient': (0.08, 0.12, 0.06, 1.0), + 'key': (0.35, 0.50, 0.28, 1.0), + 'keyHpr': (90, -75, 0), + 'fill': (0.06, 0.10, 0.04, 1.0), + 'fillHpr': (-80, -40, 0), + 'rim': (0.12, 0.20, 0.08, 1.0), + 'rimHpr': (88, -70, 0), + 'shadowCaster': False, + 'shadowRes': 512, + 'shadowArea': 120, + 'shadowDarknessFloor': 0.06, + 'fogColor': (0.04, 0.08, 0.02, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.030, + 'skyScale': (0.42, 0.62, 0.30, 1.0), + 'clearColor': (0.03, 0.05, 0.02, 1.0), + 'sunUV': (0.50, 0.50), + 'rayColor': (0.40, 0.70, 0.20, 1.0), + 'rayIntensity': 0.02, + 'bloomIntensity': 0.10, + 'bloomThreshold': 0.60, + 'exposure': 1.40, + 'hasWater': False, + 'dayNightEnabled': False, + 'cloudCoverage': 0.0, + 'cloudSpeed': 0.0, + 'cloudSharpness': 0.0, + 'turbidity': 1.0, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Cashbot HQ (exterior) ───────────────────────────────────────────────── + 'cashbot_hq': { + 'ambient': (0.06, 0.10, 0.06, 1.0), + 'key': (0.28, 0.40, 0.18, 1.0), + 'keyHpr': (90, -50, 0), + 'fill': (0.04, 0.08, 0.04, 1.0), + 'fillHpr': (-80, -25, 0), + 'rim': (0.24, 0.32, 0.10, 1.0), + 'rimHpr': (88, -45, 0), + 'shadowCaster': True, + 'shadowRes': 1024, + 'shadowArea': 160, + 'shadowResScale': 0.75, + 'shadowAreaScale': 1.20, + 'shadowFollowDist': 210.0, + 'shadowDarknessFloor': 0.06, + 'fogColor': (0.08, 0.14, 0.06, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.012, + 'skyScale': (0.48, 0.82, 0.38, 1.0), + 'clearColor': (0.06, 0.12, 0.04, 1.0), + 'sunUV': (0.50, 0.65), + 'rayColor': (0.68, 1.00, 0.38, 1.0), + 'rayIntensity': 0.08, + 'bloomIntensity': 0.24, + 'bloomThreshold': 0.58, + 'exposure': 1.25, + 'hasWater': False, + 'dayNightEnabled': False, + 'cloudCoverage': 0.85, + 'cloudSpeed': 0.40, + 'cloudSharpness': 0.10, + 'turbidity': 7.0, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Cashbot Mint interior ───────────────────────────────────────────────── + 'mint_int': { + 'ambient': (0.06, 0.10, 0.08, 1.0), + 'key': (0.32, 0.50, 0.38, 1.0), + 'keyHpr': (90, -80, 0), + 'fill': (0.04, 0.08, 0.06, 1.0), + 'fillHpr': (-80, -40, 0), + 'rim': (0.10, 0.16, 0.12, 1.0), + 'rimHpr': (88, -75, 0), + 'shadowCaster': False, + 'shadowRes': 512, + 'shadowArea': 100, + 'shadowDarknessFloor': 0.05, + 'fogColor': (0.02, 0.06, 0.04, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.025, + 'skyScale': (0.38, 0.68, 0.48, 1.0), + 'clearColor': (0.02, 0.05, 0.03, 1.0), + 'sunUV': (0.50, 0.50), + 'rayColor': (0.30, 0.80, 0.50, 1.0), + 'rayIntensity': 0.02, + 'bloomIntensity': 0.08, + 'bloomThreshold': 0.55, + 'exposure': 1.45, + 'hasWater': False, + 'dayNightEnabled': False, + 'cloudCoverage': 0.0, + 'cloudSpeed': 0.0, + 'cloudSharpness': 0.0, + 'turbidity': 1.0, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Lawbot HQ (exterior) ────────────────────────────────────────────────── + 'lawbot_hq': { + 'ambient': (0.08, 0.10, 0.22, 1.0), + 'key': (0.28, 0.36, 0.58, 1.0), + 'keyHpr': (180, -55, 0), + 'fill': (0.06, 0.08, 0.18, 1.0), + 'fillHpr': (0, -30, 0), + 'rim': (0.10, 0.14, 0.30, 1.0), + 'rimHpr': (178, -50, 0), + 'shadowCaster': False, + 'shadowRes': 512, + 'shadowArea': 180, + 'shadowDarknessFloor': 0.08, + 'fogColor': (0.18, 0.22, 0.42, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.014, + 'skyScale': (0.50, 0.62, 0.94, 1.0), + 'clearColor': (0.12, 0.16, 0.34, 1.0), + 'sunUV': (0.50, 0.70), + 'rayColor': (0.58, 0.78, 1.00, 1.0), + 'rayIntensity': 0.04, + 'bloomIntensity': 0.09, + 'bloomThreshold': 0.75, + 'exposure': 1.20, + 'hasWater': False, + 'dayNightEnabled': False, + 'cloudCoverage': 0.92, + 'cloudSpeed': 0.20, + 'cloudSharpness': 0.10, + 'turbidity': 7.5, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Lawbot Office interior ──────────────────────────────────────────────── + 'office_int': { + 'ambient': (0.18, 0.22, 0.28, 1.0), + 'key': (0.65, 0.72, 0.88, 1.0), + 'keyHpr': (0, -85, 0), + 'fill': (0.14, 0.18, 0.24, 1.0), + 'fillHpr': (180, -60, 0), + 'rim': (0.10, 0.14, 0.20, 1.0), + 'rimHpr': (90, -45, 0), + 'shadowCaster': False, + 'shadowRes': 512, + 'shadowArea': 100, + 'shadowDarknessFloor': 0.15, + 'fogColor': (0.12, 0.16, 0.24, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.018, + 'skyScale': (0.56, 0.68, 0.88, 1.0), + 'clearColor': (0.10, 0.14, 0.22, 1.0), + 'sunUV': (0.50, 0.50), + 'rayColor': (0.70, 0.82, 1.00, 1.0), + 'rayIntensity': 0.03, + 'bloomIntensity': 0.12, + 'bloomThreshold': 0.65, + 'exposure': 1.15, + 'hasWater': False, + 'dayNightEnabled': False, + 'cloudCoverage': 0.0, + 'cloudSpeed': 0.0, + 'cloudSharpness': 0.0, + 'turbidity': 1.0, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Bossbot HQ (exterior) ───────────────────────────────────────────────── + 'bossbot_hq': { + 'ambient': (0.03, 0.02, 0.02, 1.0), + 'key': (0.12, 0.07, 0.03, 1.0), + 'keyHpr': (90, -8, 0), + 'fill': (0.04, 0.03, 0.06, 1.0), + 'fillHpr': (-90, -15, 0), + 'rim': (0.06, 0.06, 0.11, 1.0), + 'rimHpr': (270, -20, 0), + 'shadowCaster': False, + 'shadowRes': 512, + 'shadowArea': 180, + 'shadowDarknessFloor': 0.02, + 'fogColor': (0.02, 0.01, 0.01, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.024, + 'skyScale': (0.20, 0.14, 0.10, 1.0), + 'clearColor': (0.01, 0.01, 0.02, 1.0), + 'sunUV': (0.50, 0.50), + 'rayColor': (0.40, 0.22, 0.10, 1.0), + 'rayIntensity': 0.00, + 'bloomIntensity': 0.00, + 'bloomThreshold': 0.99, + 'exposure': 1.50, + 'hasWater': False, + 'dayNightEnabled': False, + 'cloudCoverage': 0.99, + 'cloudSpeed': 0.10, + 'cloudSharpness': 0.05, + 'turbidity': 8.0, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Bossbot Country Club interior ───────────────────────────────────────── + 'bossbot_cc': { + 'ambient': (0.10, 0.06, 0.04, 1.0), + 'key': (0.50, 0.38, 0.22, 1.0), + 'keyHpr': (90, -60, 0), + 'fill': (0.08, 0.05, 0.03, 1.0), + 'fillHpr': (-80, -35, 0), + 'rim': (0.18, 0.12, 0.06, 1.0), + 'rimHpr': (88, -55, 0), + 'shadowCaster': False, + 'shadowRes': 512, + 'shadowArea': 100, + 'shadowDarknessFloor': 0.08, + 'fogColor': (0.08, 0.04, 0.02, 1.0), + 'fogNear': 0.0, + 'fogFar': None, + 'fogExponent': 0.018, + 'skyScale': (0.40, 0.28, 0.16, 1.0), + 'clearColor': (0.05, 0.03, 0.01, 1.0), + 'sunUV': (0.50, 0.50), + 'rayColor': (0.80, 0.55, 0.25, 1.0), + 'rayIntensity': 0.06, + 'bloomIntensity': 0.14, + 'bloomThreshold': 0.60, + 'exposure': 1.35, + 'hasWater': False, + 'dayNightEnabled': False, + 'cloudCoverage': 0.0, + 'cloudSpeed': 0.0, + 'cloudSharpness': 0.0, + 'turbidity': 1.0, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + + # ── Generic fallbacks ───────────────────────────────────────────────────── + 'playground': { + 'ambient': (0.34, 0.40, 0.54, 1.0), + 'key': (1.22, 1.14, 0.94, 1.0), + 'keyHpr': (118, -52, 0), + 'fill': (0.26, 0.34, 0.48, 1.0), + 'fillHpr': (-48, -42, 0), + 'rim': (0.14, 0.20, 0.34, 1.0), + 'rimHpr': (300, -30, 0), + 'shadowCaster': True, + 'shadowRes': 2048, + 'shadowArea': 180, + 'shadowResScale': 0.90, + 'shadowAreaScale': 1.20, + 'shadowFollowDist': 240.0, + 'shadowDarknessFloor': 0.22, + 'fogColor': (0.65, 0.78, 0.94, 1.0), + 'fogNear': 120.0, + 'fogFar': 560.0, + 'fogExponent': None, + 'skyScale': (1.00, 1.00, 1.00, 1.0), + 'clearColor': (0.42, 0.62, 0.84, 1.0), + 'sunUV': (0.62, 0.76), + 'rayColor': (1.00, 0.95, 0.74, 1.0), + 'rayIntensity': 0.38, + 'bloomIntensity': 0.36, + 'bloomThreshold': 0.58, + 'exposure': 1.00, + 'hasWater': False, + 'dayNightEnabled': True, + 'cloudCoverage': 0.35, + 'cloudSpeed': 0.65, + 'cloudSharpness': 0.50, + 'turbidity': 2.4, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + 'estate': { + 'ambient': (0.36, 0.36, 0.48, 1.0), + 'key': (1.20, 1.12, 0.96, 1.0), + 'keyHpr': (105, -48, 0), + 'fill': (0.28, 0.34, 0.46, 1.0), + 'fillHpr': (-55, -38, 0), + 'rim': (0.12, 0.18, 0.30, 1.0), + 'rimHpr': (285, -28, 0), + 'shadowCaster': True, + 'shadowRes': 2048, + 'shadowArea': 180, + 'shadowResScale': 0.90, + 'shadowAreaScale': 1.20, + 'shadowFollowDist': 240.0, + 'shadowDarknessFloor': 0.24, + 'fogColor': (0.68, 0.80, 0.94, 1.0), + 'fogNear': 120.0, + 'fogFar': 560.0, + 'fogExponent': None, + 'skyScale': (1.00, 1.00, 1.00, 1.0), + 'clearColor': (0.44, 0.64, 0.86, 1.0), + 'sunUV': (0.58, 0.74), + 'rayColor': (1.00, 0.95, 0.80, 1.0), + 'rayIntensity': 0.32, + 'bloomIntensity': 0.30, + 'bloomThreshold': 0.60, + 'exposure': 1.00, + 'hasWater': True, + 'waterColor': (0.26, 0.46, 0.64, 0.86), + 'waterReflQuality': 'low', + 'dayNightEnabled': True, + 'cloudCoverage': 0.38, + 'cloudSpeed': 0.60, + 'cloudSharpness': 0.55, + 'turbidity': 2.2, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, + 'cog': { + 'ambient': (0.32, 0.34, 0.40, 1.0), + 'key': (0.82, 0.84, 0.92, 1.0), + 'keyHpr': (90, -45, 0), + 'fill': (0.16, 0.20, 0.28, 1.0), + 'fillHpr': (-75, -30, 0), + 'rim': None, + 'shadowCaster': False, + 'shadowRes': 512, + 'shadowArea': 180, + 'shadowDarknessFloor': 0.18, + 'fogColor': (0.40, 0.42, 0.50, 1.0), + 'fogNear': 80.0, + 'fogFar': 360.0, + 'fogExponent': None, + 'skyScale': (0.82, 0.84, 0.90, 1.0), + 'clearColor': (0.32, 0.36, 0.46, 1.0), + 'sunUV': (0.50, 0.70), + 'rayColor': (0.88, 0.90, 1.00, 1.0), + 'rayIntensity': 0.12, + 'bloomIntensity': 0.10, + 'bloomThreshold': 0.70, + 'exposure': 1.00, + 'hasWater': False, + 'dayNightEnabled': False, + 'cloudCoverage': 0.75, + 'cloudSpeed': 0.50, + 'cloudSharpness': 0.20, + 'turbidity': 6.0, + 'starBrightness': 0.0, + 'moonEnabled': False, + }, +} + +# ───────────────────────────────────────────────────────────────────────────── +# Day / Night keyframe tables (unchanged from previous version) +# ───────────────────────────────────────────────────────────────────────────── +_DAY_NIGHT_KEYFRAMES: dict[str, list[tuple]] = { + 'tt': [ + (0.0, {'ambient': (0.08,0.12,0.28,1), 'key': (0.20,0.24,0.52,1), + 'keyHpr': (225,-38,0), 'fill': (0.05,0.08,0.18,1), + 'rim': (0.28,0.36,0.68,1), 'rimHpr': (222,-35,0), + 'fogColor': (0.08,0.12,0.28,1), 'fogNear': 30.0, 'fogFar': 340.0, + 'clearColor': (0.06,0.09,0.22,1), 'skyScale': (0.42,0.48,0.75,1), + 'rayColor': (0.58,0.70,1.00,1), 'rayIntensity': 0.22, + 'bloomIntensity': 0.18, 'shadowCaster': False, + 'starBrightness': 0.96, 'moonEnabled': True, 'moonDir': (180,-52,0), + 'exposure': 1.40}), + (5.5, {'ambient': (0.10,0.11,0.22,1), 'key': (0.28,0.22,0.46,1), + 'keyHpr': (80,-5,0), 'fill': (0.08,0.08,0.18,1), + 'rim': (0.24,0.20,0.46,1), 'rimHpr': (82,-4,0), + 'fogColor': (0.14,0.18,0.36,1), 'fogNear': 50.0, 'fogFar': 370.0, + 'clearColor': (0.12,0.15,0.30,1), 'skyScale': (0.52,0.55,0.80,1), + 'rayColor': (0.70,0.62,0.90,1), 'rayIntensity': 0.12, + 'bloomIntensity': 0.14, 'shadowCaster': False, + 'starBrightness': 0.32, 'moonEnabled': True, 'moonDir': (265,-38,0), + 'exposure': 1.25}), + (6.5, {'ambient': (0.26,0.18,0.20,1), 'key': (1.00,0.54,0.32,1), + 'keyHpr': (90,-10,0), 'fill': (0.22,0.16,0.32,1), + 'rim': (0.82,0.40,0.20,1), 'rimHpr': (88,-8,0), + 'fogColor': (0.88,0.56,0.35,1), 'fogNear': 70.0, 'fogFar': 420.0, + 'clearColor': (0.78,0.44,0.25,1), 'skyScale': (1.22,0.82,0.60,1), + 'rayColor': (1.00,0.64,0.30,1), 'rayIntensity': 0.56, + 'bloomIntensity': 0.50, 'shadowCaster': True, + 'starBrightness': 0.0, 'exposure': 1.05}), + (9.0, {'ambient': (0.28,0.32,0.46,1), 'key': (1.28,1.10,0.84,1), + 'keyHpr': (120,-36,0), 'fill': (0.28,0.36,0.52,1), 'rim': (0.14,0.20,0.34,1), + 'rimHpr': (300,-28,0), + 'fogColor': (0.70,0.80,0.94,1), 'fogNear': 100.0, 'fogFar': 500.0, + 'clearColor': (0.50,0.68,0.90,1), 'skyScale': (1.04,1.00,0.92,1), + 'rayColor': (1.00,0.90,0.64,1), 'rayIntensity': 0.36, + 'bloomIntensity': 0.36, 'shadowCaster': True, + 'starBrightness': 0.0, 'exposure': 1.00}), + (12.0, {'ambient': (0.34,0.40,0.54,1), 'key': (1.48,1.32,1.02,1), + 'keyHpr': (180,-86,0), 'fill': (0.34,0.44,0.62,1), 'rim': (0.18,0.24,0.40,1), + 'rimHpr': (315,-30,0), + 'fogColor': (0.76,0.85,0.96,1), 'fogNear': 115.0, 'fogFar': 580.0, + 'clearColor': (0.52,0.72,0.96,1), 'skyScale': (1.00,1.00,0.96,1), + 'rayColor': (1.00,0.96,0.80,1), 'rayIntensity': 0.34, + 'bloomIntensity': 0.42, 'shadowCaster': True, + 'starBrightness': 0.0, 'exposure': 1.00}), + (15.0, {'ambient': (0.32,0.38,0.54,1), 'key': (1.45,1.28,0.96,1), + 'keyHpr': (135,-42,0), 'fill': (0.32,0.42,0.62,1), 'rim': (0.18,0.24,0.40,1), + 'rimHpr': (315,-30,0), + 'fogColor': (0.72,0.80,0.92,1), 'fogNear': 110.0, 'fogFar': 560.0, + 'clearColor': (0.50,0.68,0.90,1), 'skyScale': (1.04,1.01,0.91,1), + 'rayColor': (1.00,0.94,0.68,1), 'rayIntensity': 0.48, + 'bloomIntensity': 0.40, 'shadowCaster': True, + 'starBrightness': 0.0, 'exposure': 1.00}), + (17.5, {'ambient': (0.36,0.26,0.28,1), 'key': (1.32,0.80,0.36,1), + 'keyHpr': (230,-22,0), 'fill': (0.26,0.20,0.40,1), + 'rim': (0.90,0.44,0.18,1), 'rimHpr': (228,-18,0), + 'fogColor': (0.90,0.62,0.36,1), 'fogNear': 60.0, 'fogFar': 410.0, + 'clearColor': (0.84,0.54,0.26,1), 'skyScale': (1.24,0.86,0.60,1), + 'rayColor': (1.00,0.66,0.28,1), 'rayIntensity': 0.70, + 'bloomIntensity': 0.60, 'shadowCaster': True, + 'starBrightness': 0.0, 'exposure': 1.02}), + (19.0, {'ambient': (0.26,0.16,0.32,1), 'key': (0.70,0.35,0.48,1), + 'keyHpr': (262,-8,0), 'fill': (0.18,0.12,0.32,1), + 'rim': (0.56,0.25,0.40,1), 'rimHpr': (260,-5,0), + 'fogColor': (0.44,0.22,0.40,1), 'fogNear': 30.0, 'fogFar': 310.0, + 'clearColor': (0.34,0.16,0.36,1), 'skyScale': (0.92,0.60,0.82,1), + 'rayColor': (0.90,0.46,0.72,1), 'rayIntensity': 0.40, + 'bloomIntensity': 0.46, 'shadowCaster': False, + 'starBrightness': 0.16, 'moonEnabled': False, 'exposure': 1.18}), + (21.0, {'ambient': (0.08,0.10,0.24,1), 'key': (0.20,0.26,0.54,1), + 'keyHpr': (225,-40,0), 'fill': (0.05,0.07,0.16,1), + 'rim': (0.28,0.34,0.66,1), 'rimHpr': (222,-38,0), + 'fogColor': (0.07,0.10,0.24,1), 'fogNear': 30.0, 'fogFar': 340.0, + 'clearColor': (0.06,0.08,0.20,1), 'skyScale': (0.40,0.44,0.72,1), + 'rayColor': (0.56,0.68,1.00,1), 'rayIntensity': 0.24, + 'bloomIntensity': 0.18, 'shadowCaster': False, + 'starBrightness': 0.92, 'moonEnabled': True, 'moonDir': (90,-55,0), + 'exposure': 1.36}), + (24.0, None), + ], + 'dd': [ + (0.0, {'ambient': (0.38,0.40,0.46,1), 'key': (0.42,0.46,0.52,1), + 'keyHpr': (0,-70,0), 'fogColor': (0.30,0.34,0.42,1), + 'fogExponent': 0.014, 'clearColor': (0.28,0.32,0.40,1), + 'skyScale': (0.58,0.62,0.70,1), 'bloomIntensity': 0.10}), + (7.0, {'ambient': (0.52,0.54,0.58,1), 'key': (0.60,0.64,0.70,1), + 'keyHpr': (0,-75,0), 'fogColor': (0.60,0.64,0.70,1), + 'fogExponent': 0.011, 'clearColor': (0.58,0.62,0.70,1), + 'skyScale': (0.78,0.82,0.88,1), 'bloomIntensity': 0.15}), + (13.0, {'ambient': (0.62,0.65,0.70,1), 'key': (0.72,0.76,0.82,1), + 'keyHpr': (0,-80,0), 'fogColor': (0.72,0.76,0.82,1), + 'fogExponent': 0.010, 'clearColor': (0.70,0.74,0.80,1), + 'skyScale': (0.84,0.87,0.92,1), 'bloomIntensity': 0.20}), + (20.0, {'ambient': (0.44,0.46,0.52,1), 'key': (0.50,0.52,0.60,1), + 'keyHpr': (270,-72,0), 'fogColor': (0.42,0.46,0.54,1), + 'fogExponent': 0.013, 'clearColor': (0.40,0.44,0.52,1), + 'skyScale': (0.70,0.72,0.80,1), 'bloomIntensity': 0.12}), + (24.0, None), + ], + 'dg': [ + (0.0, {'ambient': (0.04,0.07,0.12,1), 'key': (0.16,0.20,0.40,1), + 'keyHpr': (225,-38,0), 'fill': (0.03,0.05,0.09,1), + 'rim': (0.14,0.18,0.36,1), 'rimHpr': (222,-34,0), + 'fogColor': (0.05,0.08,0.18,1), 'fogNear': 10.0, 'fogFar': 280.0, + 'clearColor': (0.04,0.06,0.14,1), 'skyScale': (0.34,0.42,0.68,1), + 'rayColor': (0.56,0.70,1.00,1), 'rayIntensity': 0.18, + 'bloomIntensity': 0.14, 'shadowCaster': False, + 'starBrightness': 0.76, 'moonEnabled': True, 'moonDir': (180,-55,0)}), + (6.0, {'ambient': (0.20,0.16,0.18,1), 'key': (0.86,0.46,0.22,1), + 'keyHpr': (90,-8,0), 'fill': (0.16,0.12,0.26,1), + 'rim': (0.68,0.34,0.14,1), 'rimHpr': (88,-6,0), + 'fogColor': (0.80,0.50,0.28,1), 'fogNear': 50.0, 'fogFar': 360.0, + 'clearColor': (0.70,0.38,0.18,1), 'skyScale': (1.18,0.76,0.52,1), + 'rayColor': (1.00,0.60,0.28,1), 'rayIntensity': 0.50, + 'bloomIntensity': 0.44, 'shadowCaster': True, + 'starBrightness': 0.0, 'moonEnabled': False}), + (11.0, {'ambient': (0.18,0.25,0.42,1), 'key': (1.20,1.10,0.90,1), + 'keyHpr': (180,-82,0), 'fill': (0.22,0.32,0.52,1), + 'rim': (0.10,0.16,0.30,1), 'rimHpr': (0,55,0), + 'fogColor': (0.62,0.80,1.00,1), 'fogNear': 120.0, 'fogFar': 540.0, + 'clearColor': (0.36,0.62,1.00,1), 'skyScale': (0.88,0.96,1.14,1), + 'rayColor': (1.00,1.00,0.88,1), 'rayIntensity': 0.58, + 'bloomIntensity': 0.44, 'shadowCaster': True, + 'starBrightness': 0.0, 'moonEnabled': False}), + (17.0, {'ambient': (0.28,0.22,0.24,1), 'key': (1.10,0.70,0.30,1), + 'keyHpr': (235,-20,0), 'fill': (0.20,0.16,0.32,1), + 'rim': (0.78,0.36,0.14,1), 'rimHpr': (232,-16,0), + 'fogColor': (0.84,0.56,0.32,1), 'fogNear': 40.0, 'fogFar': 360.0, + 'clearColor': (0.76,0.46,0.20,1), 'skyScale': (1.18,0.82,0.54,1), + 'rayColor': (1.00,0.62,0.24,1), 'rayIntensity': 0.60, + 'bloomIntensity': 0.52, 'shadowCaster': True, + 'starBrightness': 0.0, 'moonEnabled': False}), + (20.0, {'ambient': (0.06,0.08,0.18,1), 'key': (0.18,0.22,0.44,1), + 'keyHpr': (225,-40,0), 'fill': (0.04,0.06,0.12,1), + 'rim': (0.22,0.26,0.52,1), 'rimHpr': (222,-38,0), + 'fogColor': (0.05,0.08,0.18,1), 'fogNear': 10.0, 'fogFar': 280.0, + 'clearColor': (0.04,0.06,0.14,1), 'skyScale': (0.36,0.44,0.70,1), + 'rayColor': (0.56,0.70,1.00,1), 'rayIntensity': 0.20, + 'bloomIntensity': 0.16, 'shadowCaster': False, + 'starBrightness': 0.42, 'moonEnabled': True, 'moonDir': (120,-48,0)}), + (24.0, None), + ], + 'mm': [ + (0.0, {'ambient': (0.24,0.10,0.22,1), 'key': (0.26,0.14,0.36,1), + 'keyHpr': (270,-5,0), 'fill': (0.12,0.06,0.22,1), + 'rim': (0.30,0.14,0.28,1), 'rimHpr': (268,-3,0), + 'fogColor': (0.22,0.10,0.28,1), 'fogNear': 5.0, 'fogFar': 200.0, + 'clearColor': (0.16,0.08,0.24,1), 'skyScale': (0.60,0.28,0.56,1), + 'rayColor': (0.72,0.32,0.80,1), 'rayIntensity': 0.24, + 'bloomIntensity': 0.28, 'shadowCaster': False, + 'starBrightness': 0.58, 'moonEnabled': True, 'moonDir': (220,-42,0)}), + (6.0, {'ambient': (0.36,0.22,0.16,1), 'key': (0.80,0.38,0.10,1), + 'keyHpr': (90,-10,0), 'fill': (0.18,0.12,0.26,1), + 'rim': (0.60,0.28,0.10,1), 'rimHpr': (88,-8,0), + 'fogColor': (0.72,0.36,0.14,1), 'fogNear': 10.0, 'fogFar': 290.0, + 'clearColor': (0.66,0.32,0.12,1), 'skyScale': (1.14,0.60,0.34,1), + 'rayColor': (1.00,0.52,0.18,1), 'rayIntensity': 0.60, + 'bloomIntensity': 0.52, 'shadowCaster': True, + 'starBrightness': 0.0, 'moonEnabled': False}), + (14.0, {'ambient': (0.42,0.28,0.30,1), 'key': (1.10,0.50,0.18,1), + 'keyHpr': (260,-14,0), 'fill': (0.22,0.18,0.40,1), + 'rim': (0.90,0.40,0.12,1), 'rimHpr': (258,-8,0), + 'fogColor': (0.80,0.44,0.20,1), 'fogNear': 10.0, 'fogFar': 330.0, + 'clearColor': (0.88,0.46,0.18,1), 'skyScale': (1.30,0.72,0.48,1), + 'rayColor': (1.00,0.58,0.22,1), 'rayIntensity': 0.70, + 'bloomIntensity': 0.56, 'shadowCaster': True, + 'starBrightness': 0.0, 'moonEnabled': False}), + (20.0, {'ambient': (0.28,0.12,0.32,1), 'key': (0.52,0.22,0.50,1), + 'keyHpr': (270,-6,0), 'fill': (0.14,0.08,0.28,1), + 'rim': (0.44,0.18,0.42,1), 'rimHpr': (268,-4,0), + 'fogColor': (0.34,0.14,0.38,1), 'fogNear': 5.0, 'fogFar': 220.0, + 'clearColor': (0.26,0.10,0.34,1), 'skyScale': (0.80,0.40,0.80,1), + 'rayColor': (0.80,0.38,0.90,1), 'rayIntensity': 0.32, + 'bloomIntensity': 0.38, 'shadowCaster': False, + 'starBrightness': 0.24, 'moonEnabled': True, 'moonDir': (240,-36,0)}), + (24.0, None), + ], + 'br': [ + (0.0, {'ambient': (0.28,0.36,0.54,1), 'key': (0.40,0.50,0.70,1), + 'keyHpr': (180,-10,0), 'fogColor': (0.40,0.52,0.70,1), + 'fogExponent': 0.020, 'clearColor': (0.36,0.46,0.66,1), + 'skyScale': (0.60,0.72,0.94,1), 'bloomIntensity': 0.12}), + (12.0, {'ambient': (0.44,0.52,0.70,1), 'key': (0.64,0.74,0.92,1), + 'keyHpr': (180,-18,0), 'fogColor': (0.68,0.78,0.94,1), + 'fogExponent': 0.016, 'clearColor': (0.62,0.74,0.92,1), + 'skyScale': (0.78,0.88,1.08,1), 'bloomIntensity': 0.22}), + (18.0, {'ambient': (0.32,0.38,0.58,1), 'key': (0.48,0.56,0.78,1), + 'keyHpr': (270,-12,0), 'fogColor': (0.50,0.60,0.80,1), + 'fogExponent': 0.018, 'clearColor': (0.46,0.58,0.78,1), + 'skyScale': (0.68,0.80,1.02,1), 'bloomIntensity': 0.16}), + (24.0, None), + ], + 'dl': [ + (0.0, {'ambient': (0.06,0.08,0.16,1), 'key': (0.40,0.46,0.68,1), + 'keyHpr': (225,-55,0), 'fill': (0.04,0.06,0.12,1), + 'rim': (0.30,0.36,0.58,1), 'rimHpr': (220,-50,0), + 'fogColor': (0.05,0.07,0.14,1), 'fogExponent': 0.006, + 'clearColor': (0.03,0.04,0.10,1), 'skyScale': (0.42,0.48,0.74,1), + 'rayColor': (0.58,0.70,1.00,1), 'rayIntensity': 0.32, + 'bloomIntensity': 0.20, 'shadowCaster': True}), + (3.5, {'ambient': (0.04,0.05,0.12,1), 'key': (0.20,0.24,0.48,1), + 'keyHpr': (270,-20,0), 'fill': (0.02,0.03,0.08,1), + 'rim': (0.16,0.20,0.44,1), 'rimHpr': (268,-18,0), + 'fogColor': (0.03,0.04,0.10,1), 'fogExponent': 0.008, + 'clearColor': (0.02,0.02,0.07,1), 'skyScale': (0.28,0.34,0.60,1), + 'rayColor': (0.46,0.58,0.90,1), 'rayIntensity': 0.16, + 'bloomIntensity': 0.12, 'shadowCaster': False}), + (5.0, {'ambient': (0.08,0.06,0.16,1), 'key': (0.24,0.18,0.44,1), + 'keyHpr': (80,-6,0), 'fill': (0.06,0.04,0.12,1), + 'rim': (0.20,0.14,0.40,1), 'rimHpr': (78,-4,0), + 'fogColor': (0.08,0.06,0.18,1), 'fogExponent': 0.007, + 'clearColor': (0.06,0.05,0.16,1), 'skyScale': (0.40,0.36,0.70,1), + 'rayColor': (0.62,0.52,0.90,1), 'rayIntensity': 0.14, + 'bloomIntensity': 0.14, 'shadowCaster': False}), + (12.0, {'ambient': (0.10,0.12,0.22,1), 'key': (0.38,0.42,0.64,1), + 'keyHpr': (180,-50,0), 'fill': (0.06,0.08,0.16,1), + 'rim': (0.28,0.32,0.56,1), 'rimHpr': (178,-46,0), + 'fogColor': (0.07,0.09,0.18,1), 'fogExponent': 0.006, + 'clearColor': (0.06,0.07,0.16,1), 'skyScale': (0.48,0.54,0.80,1), + 'rayColor': (0.56,0.68,1.00,1), 'rayIntensity': 0.28, + 'bloomIntensity': 0.18, 'shadowCaster': True}), + (20.0, {'ambient': (0.06,0.08,0.16,1), 'key': (0.38,0.44,0.66,1), + 'keyHpr': (135,-42,0), 'fill': (0.04,0.06,0.12,1), + 'rim': (0.28,0.34,0.56,1), 'rimHpr': (132,-38,0), + 'fogColor': (0.05,0.07,0.14,1), 'fogExponent': 0.006, + 'clearColor': (0.03,0.04,0.10,1), 'skyScale': (0.42,0.48,0.74,1), + 'rayColor': (0.58,0.70,1.00,1), 'rayIntensity': 0.30, + 'bloomIntensity': 0.20, 'shadowCaster': True}), + (24.0, None), + ], + 'gs': [ + (0.0, {'ambient': (0.10,0.12,0.20,1), 'key': (0.18,0.20,0.36,1), + 'keyHpr': (225,-32,0), 'fogColor': (0.14,0.16,0.26,1), + 'fogNear': 20.0, 'fogFar': 280.0, 'clearColor': (0.12,0.14,0.22,1), + 'bloomIntensity': 0.12, 'shadowCaster': False, + 'starBrightness': 0.78, 'moonEnabled': True, 'moonDir': (0,-60,0)}), + (7.0, {'ambient': (0.28,0.26,0.24,1), 'key': (0.88,0.58,0.26,1), + 'keyHpr': (90,-12,0), 'fogColor': (0.72,0.60,0.42,1), + 'fogNear': 50.0, 'fogFar': 320.0, 'clearColor': (0.62,0.50,0.34,1), + 'bloomIntensity': 0.36, 'shadowCaster': True, + 'starBrightness': 0.0, 'moonEnabled': False}), + (11.0, {'ambient': (0.30,0.32,0.40,1), 'key': (1.10,1.00,0.82,1), + 'keyHpr': (130,-48,0), 'fogColor': (0.78,0.76,0.68,1), + 'fogNear': 60.0, 'fogFar': 380.0, 'clearColor': (0.56,0.64,0.72,1), + 'bloomIntensity': 0.26, 'shadowCaster': True, + 'starBrightness': 0.0, 'moonEnabled': False}), + (18.0, {'ambient': (0.28,0.22,0.18,1), 'key': (1.00,0.58,0.22,1), + 'keyHpr': (240,-18,0), 'fogColor': (0.72,0.46,0.24,1), + 'fogNear': 40.0, 'fogFar': 320.0, 'clearColor': (0.66,0.40,0.18,1), + 'bloomIntensity': 0.44, 'shadowCaster': True, + 'starBrightness': 0.0, 'moonEnabled': False}), + (21.0, {'ambient': (0.10,0.12,0.20,1), 'key': (0.20,0.22,0.40,1), + 'keyHpr': (225,-34,0), 'fogColor': (0.12,0.14,0.24,1), + 'fogNear': 20.0, 'fogFar': 280.0, 'clearColor': (0.10,0.12,0.20,1), + 'bloomIntensity': 0.14, 'shadowCaster': False, + 'starBrightness': 0.68, 'moonEnabled': True, 'moonDir': (60,-56,0)}), + (24.0, None), + ], +} + +# ───────────────────────────────────────────────────────────────────────────── +# Hood ID + Zone ID → profile key mapping +# ───────────────────────────────────────────────────────────────────────────── + +_HOOD_ID_MAP: dict[int, str] | None = None +_ZONE_ID_MAP: dict[int, str] = {} # populated lazily + + +def _buildHoodIdMap() -> dict[int, str]: + from toontown.toonbase import ToontownGlobals as TG + m = { + TG.ToontownCentral: 'tt', + TG.DonaldsDock: 'dd', + TG.MinniesMelodyland: 'mm', + TG.DaisyGardens: 'dg', + TG.TheBrrrgh: 'br', + TG.DonaldsDreamland: 'dl', + TG.GoofySpeedway: 'gs', + TG.BossbotHQ: 'bossbot_hq', + TG.SellbotHQ: 'sellbot_hq', + TG.CashbotHQ: 'cashbot_hq', + TG.LawbotHQ: 'lawbot_hq', + } + for attr in ('MyEstate', 'Estate', 'ToonEstate'): + val = getattr(TG, attr, None) + if val is not None: + m[val] = 'estate' + return m + + +def _buildZoneIdMap() -> dict[int, str]: + """Map individual zone IDs to specific sub-profiles.""" + from toontown.toonbase import ToontownGlobals as TG + + def _zr(tg_attr: str) -> int | None: + return getattr(TG, tg_attr, None) + + z: dict[int, str] = {} + + # TT streets + for attr in ('SillyStreet', 'LoopyLane', 'PunchlinePlace'): + v = _zr(attr) + if v: z[v] = 'tt_street' + + # DD streets + for attr in ('BarnacleBoulevard', 'SeaweedStreet', 'LighthouseLane'): + v = _zr(attr) + if v: z[v] = 'dd_street' + + # BR streets + for attr in ('SleetStreet', 'WalrusWay', 'PolarPlace'): + v = _zr(attr) + if v: z[v] = 'br_street' + + # MM streets + for attr in ('AltoAvenue', 'BaritoneBoulevard', 'TenorTerrace'): + v = _zr(attr) + if v: z[v] = 'mm_street' + + # DG streets + for attr in ('ElmStreet', 'MapleStreet', 'OakStreet'): + v = _zr(attr) + if v: z[v] = 'dg_street' + + # DDL streets + for attr in ('LullabyLane', 'PajamaPlace'): + v = _zr(attr) + if v: z[v] = 'dl_street' + + # Interiors + for attr in ('SellbotFactoryInt', 'SellbotLegFactoryInt', + 'LawbotFactoryInt'): + v = _zr(attr) + if v: z[v] = 'factory_int' + + for attr in ('CashbotMintIntA', 'CashbotMintIntB', 'CashbotMintIntC'): + v = _zr(attr) + if v: z[v] = 'mint_int' + + for attr in ('LawbotOfficeInt',): + v = _zr(attr) + if v: z[v] = 'office_int' + + for attr in ('BossbotCountryClubIntA', 'BossbotCountryClubIntB', + 'BossbotCountryClubIntC'): + v = _zr(attr) + if v: z[v] = 'bossbot_cc' + + for attr in ('GolfZone',): + v = _zr(attr) + if v: z[v] = 'golf_course' + + return z + + +def _resolveStyle(style: str, hoodId: int | None, + zoneId: int | None = None) -> str: + global _HOOD_ID_MAP, _ZONE_ID_MAP + + # Zone ID takes priority (most specific). + if zoneId is not None: + if not _ZONE_ID_MAP: + try: + _ZONE_ID_MAP = _buildZoneIdMap() + except Exception: + pass + res = _ZONE_ID_MAP.get(zoneId) + if res and res in _ZONE_PROFILES: + return res + # Fallback to the canonical branch zone (e.g. street segment zone 4101 -> branch 4100) + branchZone = zoneId - (zoneId % 100) + res = _ZONE_ID_MAP.get(branchZone) + if res and res in _ZONE_PROFILES: + return res + + # Hood ID next. + if hoodId is not None: + if _HOOD_ID_MAP is None: + try: + _HOOD_ID_MAP = _buildHoodIdMap() + except Exception: + _HOOD_ID_MAP = {} + res = _HOOD_ID_MAP.get(hoodId) + if res and res in _ZONE_PROFILES: + return res + + return style if style in _ZONE_PROFILES else 'playground' + + +# ───────────────────────────────────────────────────────────────────────────── +# Module-level shared state +# ───────────────────────────────────────────────────────────────────────────── + +_refCount = 0 +_activeStyle = 'playground' +_activeGeom: NodePath | None = None + +# Light rig +_lightRig: NodePath | None = None +_renderLights: list[NodePath] = [] +_keyLightNp: NodePath | None = None +_keyCastsShadows = False +_shadowFocusPos: Vec3 | None = None +_shadowSceneRadius = 180.0 +_shadowMapRes = 1024 +_shadowFilmArea = 180.0 +_shadowFollowDist = 220.0 +_prevShadowCasterState: bool | None = None +_sunDirWorld: Vec3 | None = None + +# Fog +_fogNode: Fog | None = None + +# Sky / window +_skyWasDimmed = False +_prevClearColor: tuple | None = None + +# Post-process: CommonFilters bloom + render2dp sun-ray overlay (no scene RTT compositor) +_godRaysCard: NodePath | None = None +_godRaysShader: Shader | None = None +_bloomFilters: Any = None + +# Water reflections +_waterSetups: list[dict] = [] +_waterShader: Shader | None = None + +# Procedural sky +_proceduralSky: Any = None # ProceduralSky instance + +# Day / night +_timeOfDay = 12.0 +_dayNightSpeed = 1.0 / 60.0 +_dayNightAccum = 0.0 +_godRaysTime = 0.0 + +_godRaysTaskName = 'outdoorLightingTask' + +# Street lamp / lantern point lights (active at night) +_lampLights: list[NodePath] = [] # PointLight NodePaths attached to _lightRig +_lampGeomNps: list[NodePath] = [] # Cached lamp post positions scanned from geometry +_lampLightsActive: bool = False + +# Lamp node name keywords – any node whose name contains one of these is treated as a light source +_LAMP_KEYWORDS: tuple = ( + 'lamp', 'lantern', 'streetlight', 'lightpole', 'lamp_post', 'lamppost', + 'prop_lamp', 'prop_lantern', 'light_pole', 'gazebo_lamp', 'post_lamp', + 'street_lamp', 'streetlamp', 'gas_lamp', 'gaslight', 'torch', + 'light_fixture', 'park_lamp', 'lampshade', 'bulb', + # Additional Toontown prop/DNA names that serve as light sources + 'prop_street_lamp', 'prop_gas_lamp', 'streetlamp_post', + 'light_post', 'prop_lightpost', 'lamp_base', 'glowing', 'glow', + 'firefly', 'moonflower', 'lanternpost', 'hanging_lamp', 'ceiling_light', + 'wall_light', 'window_light', 'neon', 'sign_light', +) +_LAMP_LIGHT_LIMIT = 80 # max point lights to create (performance guard) + +# IMPORTANT: +# In this codebase, BitMask32.bit(5) is OTPRender.EnviroCameraBitmask and +# `OTPBase.setupEnviroCamera()` does `render.hide(EnviroCameraBitmask)`. +# Using bit(5) for shadow cameras makes the shadow pass see an empty scene. +_SHADOW_CAM_MASK = getattr(_OTPRender, 'ShadowCameraBitmask', BitMask32.bit(2)) + +# Temporary safety: do not rely on ShadowCameraBitmask-only visibility for the +# shadow map camera. Some scenes use draw masks in ways that can make the +# shadow pass see nothing when restricted to a single bit. We keep the mask +# constant for future use, but avoid forcing the shadow camera to use it. + +_SHADER_DIR_CANDIDATES = ( + os.path.join(os.path.dirname(__file__), '..', 'shaders'), + os.path.join(os.path.dirname(__file__), 'shaders'), + os.path.join(os.path.dirname(__file__), '..', '..', 'shaders'), +) +_SHADER_DIR = next((d for d in _SHADER_DIR_CANDIDATES if os.path.exists(d)), _SHADER_DIR_CANDIDATES[0]) +_LEGACY_VERT = os.path.join(_SHADER_DIR, 'sunrays.vert.glsl') +_LEGACY_FRAG = os.path.join(_SHADER_DIR, 'sunrays.frag.glsl') +_WATER_VERT = os.path.join(_SHADER_DIR, 'water.vert.glsl') +_WATER_FRAG = os.path.join(_SHADER_DIR, 'water.frag.glsl') + +_WATER_PATTERNS = ( + 'water', 'pond', 'ocean', 'sea', 'lake', 'fountain', + 'puddle', 'river', 'stream', 'brook', 'lagoon', 'tide', +) + +_rigRebuildPending: dict | None = None + +# ───────────────────────────────────────────────────────────────────────────── +# Settings helpers +# ───────────────────────────────────────────────────────────────────────────── + +def _wantFx() -> bool: + _syncBase() + if _OUTDOOR_SHADER_BISECT_LEVEL < 1: + return False + try: + val = _getSettingValue('want-modern-outdoor-lighting', None) + if val is not None: + return bool(val) + except Exception: + pass + return ConfigVariableBool('want-modern-outdoor-lighting', True).value + + +def _settingsBool(key: str, default: bool) -> bool: + try: + return bool(_getSettingValue(key, default)) + except Exception: + return default + + +def _settingsFloat(key: str, default: float) -> float: + try: + return float(_getSettingValue(key, default)) + except Exception: + return default + + +def _settingsStr(key: str, default: str) -> str: + try: + v = _getSettingValue(key, default) + return str(v) if v is not None else default + except Exception: + return default + + +def _wantDayNight() -> bool: + try: + val = _getSettingValue('want-day-night-cycle', None) + if val is not None: + return bool(val) + except Exception: + pass + return ConfigVariableBool('want-day-night-cycle', False).value + + +def _wantBloom() -> bool: + return _bisectAllows(7) and _settingsBool('lighting-bloom-enabled', True) + + +def _wantWater() -> bool: + return _settingsBool('want-water-reflections', True) + + +_cachedSupportsBasicShaders: bool | None = None + + +def _supportsBasicShaders() -> bool: + """Returns whether the active GSG reports basic shader support. + + Shadow mapping requires shader support; when unavailable, we fall back to + OTP drop-shadows (blob shadows) instead of attempting shadow buffers. + """ + global _cachedSupportsBasicShaders + if _cachedSupportsBasicShaders is not None: + return _cachedSupportsBasicShaders + + # FIRST: If we have an active GSG, it is the absolute source of truth. + # If the C++ engine reports False (due to e.g. missing Cg support in build), + # we MUST respect it because the C++ engine won't allocate shadow buffers. + try: + if getattr(base, 'win', None): + gsg = base.win.getGsg() + if gsg: + supports_basic = bool(gsg.getSupportsBasicShaders()) + _cachedSupportsBasicShaders = supports_basic + if _debugEnabled(): + _dbg(f"Active GSG reports getSupportsBasicShaders() = {supports_basic}") + return supports_basic + except Exception: + pass + + # Check Panda3D version - 1.11.0 has known shader API bugs + panda_version = '' + try: + from panda3d.core import PandaSystem + panda_version = PandaSystem.getGlobalPtr().getVersionString() + if _debugEnabled(): + _dbg(f"Panda3D version: {panda_version}") + except Exception: + pass + + # SECOND: Standard detection for other versions / fallback when GSG is not yet open + gpu_vendor = '' + gpu_renderer = '' + + try: + if getattr(base, 'win', None): + gsg = base.win.getGsg() + if gsg: + for vendor_attr in ['getVendor', 'getDriverVendor']: + try: + gpu_vendor = str(getattr(gsg, vendor_attr, lambda: '')()) + if gpu_vendor and gpu_vendor != 'unknown': + break + except Exception: + pass + + for renderer_attr in ['getRenderer', 'getDriverRenderer']: + try: + gpu_renderer = str(getattr(gsg, renderer_attr, lambda: '')()) + if gpu_renderer and gpu_renderer != 'unknown': + break + except Exception: + pass + + if _debugEnabled(): + _dbg(f"GPU Detection: vendor='{gpu_vendor}', renderer='{gpu_renderer}'") + except Exception as e: + if _debugEnabled(): + _dbg(f"GPU detection error: {e}") + + # NVIDIA RTX GPUs definitely support shaders physically + if 'NVIDIA' in gpu_vendor and 'RTX' in gpu_renderer: + if _debugEnabled(): + _dbg(f"NVIDIA RTX GPU DETECTED: {gpu_renderer}") + return True + + is_modern_gpu = ('NVIDIA' in gpu_vendor or 'AMD' in gpu_vendor or 'Intel' in gpu_vendor) + if is_modern_gpu: + return True + + # Optimistic default when GSG is not yet initialized + if gpu_vendor or gpu_renderer: + return True + + return True + + +def _wantDynamicShadows() -> bool: + """Determine if dynamic shadow maps should be used. + + ULTRA-AGGRESSIVE VERSION: Always enable shadows for modern GPUs. + """ + # Check user preference first + want_shadows = _settingsBool('dynamic-shadows', True) + if not want_shadows: + if _debugEnabled(): + _dbg("Dynamic shadows disabled by user setting") + return False + + # Check shadow quality setting + shadow_quality = _shadowQuality() + if shadow_quality == 'off': + if _debugEnabled(): + _dbg(f"Dynamic shadows disabled: shadow-quality={shadow_quality}") + return False + + # With our aggressive _supportsBasicShaders(), this should return True for RTX GPUs + shaders_supported = _supportsBasicShaders() + + if not shaders_supported: + # Last resort: check config force options + force_shaders = _settingsBool('force-shader-support', False) + bypass_check = _settingsBool('shadow-bypass-shader-check', False) + + if force_shaders or bypass_check: + if _debugEnabled(): + _dbg(f"FORCING shadow maps: force-shader-support={force_shaders}, shadow-bypass-shader-check={bypass_check}") + return True + + if _debugEnabled(): + _dbg("Dynamic shadows disabled: shaders not supported and no force options enabled") + return False + + return True + + +def _wantSpecularHighlights() -> bool: + # Specular in Panda3D auto-shaders can produce camera-dependent color shifts + # on some legacy content/shader-generator combinations (often perceived as + # an RGB "sheen" on flat vertical surfaces). Keep it opt-in by default. + return _settingsBool('lighting-specular-enabled', False) + + +def _wantTonemap() -> bool: + return _settingsBool('lighting-tonemap-enabled', True) + + +def _wantProceduralSky() -> bool: + if _OUTDOOR_SHADER_BISECT_LEVEL < 1: + return False + return _bisectAllows(6) and _settingsBool('want-procedural-sky', True) + + +def _wantVignette() -> bool: + return _settingsBool('lighting-vignette-enabled', False) + + +def _intensityScale() -> float: + return max(0.1, min(2.0, _settingsFloat('lighting-intensity', 1.0))) + + +def _colorTempBias() -> float: + return max(-1.0, min(1.0, _settingsFloat('lighting-color-temp', 0.0))) + + +def _dayNightSpeedMultiplier() -> float: + return max(0.0, _settingsFloat('day-night-speed', 1.0)) + + +def _vignetteStrength() -> float: + if not _wantVignette(): + return 0.0 + return max(0.0, min(1.0, _settingsFloat('lighting-vignette-strength', 0.25))) + + +def _shadowQuality() -> str: + return _settingsStr('shadow-quality', 'high') # 'off' | 'low' | 'medium' | 'high' + + +def _cloudQuality() -> str: + return _settingsStr('sky-cloud-quality', 'high') # 'off' | 'low' | 'medium' | 'high' + + +def _fogDensityMult() -> float: + return max(0.1, min(3.0, _settingsFloat('fog-density-multiplier', 1.0))) + + +# ───────────────────────────────────────────────────────────────────────────── +# Math / colour helpers +# ───────────────────────────────────────────────────────────────────────────── + +def _clamp(v: float, lo: float, hi: float) -> float: + return max(lo, min(hi, v)) + + +def _lerpF(a: float, b: float, t: float) -> float: + return a + (b - a) * t + + +def _lerpT(a: tuple | None, b: tuple | None, t: float) -> tuple | None: + if a is None and b is None: + return None + if a is None: + a = tuple(0.0 for _ in b) + if b is None: + b = tuple(0.0 for _ in a) + return tuple(_lerpF(float(ai), float(bi), t) for ai, bi in zip(a, b)) + + +def _applyColorTemp(color: tuple) -> tuple: + bias = _colorTempBias() + r, g, b, a = color + if bias > 0: + r = min(1.0, r + bias * 0.08) + b = max(0.0, b - bias * 0.06) + elif bias < 0: + b = min(1.0, b - bias * 0.08) + r = max(0.0, r + bias * 0.06) + return (r, g, b, a) + + +def _scaleColor(color: tuple, scale: float) -> tuple: + return (color[0] * scale, color[1] * scale, color[2] * scale, color[3]) + + +def _desaturateRGBA(c: tuple, amt: float) -> tuple: + """Desaturate an RGBA tuple toward gray by amt in [0,1].""" + try: + r, g, b, a = float(c[0]), float(c[1]), float(c[2]), float(c[3]) + except Exception: + return c + amt = _clamp(float(amt), 0.0, 1.0) + gray = (r + g + b) / 3.0 + r = r * (1.0 - amt) + gray * amt + g = g * (1.0 - amt) + gray * amt + b = b * (1.0 - amt) + gray * amt + return (r, g, b, a) + + +def _clampChromaRGBA(c: tuple, max_chroma: float) -> tuple: + """Clamp chroma to avoid extreme channel separation on directionals. + + This reduces camera-dependent RGB banding on flat/billboarded geometry when + multiple colored directionals overlap (key/fill/rim). + """ + try: + r, g, b, a = float(c[0]), float(c[1]), float(c[2]), float(c[3]) + except Exception: + return c + max_chroma = max(0.0, float(max_chroma)) + gray = (r + g + b) / 3.0 + dr, dg, db = r - gray, g - gray, b - gray + chroma = max(abs(dr), abs(dg), abs(db)) + if chroma <= max_chroma or chroma <= 1e-8: + return (r, g, b, a) + s = max_chroma / chroma + return (gray + dr * s, gray + dg * s, gray + db * s, a) + + +def _sunDirFromHpr(hpr: tuple[float, float, float]) -> Vec3: + """Compute a stable sun direction from (H, P, R). + + This project’s zone profiles historically use a sign convention where a + *negative* pitch indicates a sun above the horizon. Panda3D DirectionalLight + expects a direction vector pointing *where the light shines* (rays travel), + so we flip pitch when converting profile HPR -> direction. + """ + try: + h, p, r = float(hpr[0]), float(hpr[1]), float(hpr[2]) + except Exception: + h, p, r = 0.0, -45.0, 0.0 + + # Enforce a minimum downward pitch so ground/roads receive light. + # Negative pitch points downward (shining down on the scene). + if p > -18.0: + p = -18.0 + + q = Quat() + q.setHpr(Vec3(h, p, r)) + d = q.getForward() + try: + d.normalize() + except Exception: + pass + return d + + +def _scaledShadowRes(spec: dict) -> int: + q = _shadowQuality() + if q == 'off': + return 0 + base_res = int(spec.get('shadowRes', 1024)) + scale = float(spec.get('shadowResScale', 1.0)) + if q == 'low': + scale *= 0.25 + elif q == 'medium': + scale *= 0.50 + res = int(round(base_res * _clamp(scale, 0.25, 2.0))) + res = int(_clamp(res, 512, 4096)) + valid = (512, 1024, 2048, 4096) + return min(valid, key=lambda v: abs(v - res)) + + +# ───────────────────────────────────────────────────────────────────────────── +# Specular + lighting fix helpers +# ───────────────────────────────────────────────────────────────────────────── + +_SPEC_TAG = 'osl_added_material' +_LIGHTON_TAG = 'osl_cleared_lightoff' +_SHADOW_HIDE_TAG = 'osl_hide_from_shadow' + + +def _applyDefaultSpecularMaterial(np: NodePath) -> None: + if not _wantSpecularHighlights() or np is None or np.isEmpty(): + return + try: + if np.hasMaterial(): + return + except Exception: + try: + if np.getMaterial() is not None: + return + except Exception: + pass + try: + if np.getPythonTag(_SPEC_TAG): + return + except Exception: + pass + try: + m = Material() + m.setSpecular(Vec4(0.22, 0.22, 0.22, 1.0)) + m.setShininess(18.0) + np.setMaterial(m, 1) + np.setPythonTag(_SPEC_TAG, True) + except Exception: + pass + + +def _clearDefaultSpecularMaterial(np: NodePath) -> None: + if np is None or np.isEmpty(): + return + try: + if not np.getPythonTag(_SPEC_TAG): + return + except Exception: + return + try: + np.clearMaterial() + except Exception: + pass + try: + np.clearPythonTag(_SPEC_TAG) + except Exception: + pass + + +def _isFoliageOrTree(np: NodePath) -> bool: + if np is None or np.isEmpty(): + return False + try: + p = np + while p and not p.isEmpty(): + name = p.getName().lower() + if any(k in name for k in ['tree', 'foliage', 'flower', 'shrub', 'plant', 'grass', 'leaf', 'leaves', 'prop_green_tree', 'bark', 'trunk', 'prop_dg_flower', 'prop_tree']): + return True + if p.getName() == 'render': + break + p = p.getParent() + except Exception: + pass + return False + + +def _forceLightingOnSubtree(root: NodePath) -> None: + """Clear LightAttrib overrides that disable per-pixel lighting. + + Some geometry has baked vertex colours and explicit "light off" attribs + that prevent the dynamic sun from reaching them. Clearing those lets + the key/fill/rim lights illuminate the full zone. + """ + if root is None or root.isEmpty(): + return + # Clear on the full subtree (not just the root). Some DNA content attaches + # LightAttrib / LightOff overrides on intermediate nodes, which will still + # cancel our lights if we only clear at the top. + try: + nodes = root.findAllMatches('**') + for i in range(nodes.getNumPaths()): + np = nodes.getPath(i) + try: + if _isFoliageOrTree(np): + try: + np.setLightOff(101) + except Exception: + pass + continue + # IMPORTANT: + # Billboarded/card geometry will change orientation with the camera. + # Directional lighting on such geometry is therefore inherently + # camera-dependent and can appear as an RGB "shift" as the camera turns. + # Do not forcibly clear LightOff/LightAttrib on these nodes. + try: + if np.hasEffect(BillboardEffect.getClassType()): + continue + except Exception: + pass + if hasattr(np, 'clearLightOff'): + np.clearLightOff() + try: + np.clearAttrib(LightAttrib.getClassType()) + except Exception: + pass + except Exception: + pass + except Exception: + # Fallback: at least try the root. + try: + if hasattr(root, 'clearLightOff'): + root.clearLightOff() + try: + root.clearAttrib(LightAttrib.getClassType()) + except Exception: + pass + except Exception: + pass + + +def _hidePlaneLikeCastersFromShadow(root: NodePath) -> None: + """Exclude 2D billboard / card geometry from the shadow CAMERA PASS only. + + 2D foliage planes, building facades, and billboard sprites should NOT + cast sun-blocking shadows (they would create giant flat shadow rectangles). + They still RECEIVE shadows, and they are still lit by the sun. + + We use the dedicated _SHADOW_CAM_MASK so the shadow camera skips them + while the regular render camera sees them normally. + """ + if root is None or root.isEmpty(): + return + + # NOTE: + # This project uses OTP camera bitmasks, and restricting the shadow camera to a + # single bitmask can result in an empty shadow pass on some scenes. Until we + # have confirmed stable shadow visibility everywhere, we avoid hiding geometry + # from the shadow pass via bitmask tricks. (We'll reintroduce this once base + # shadows are confirmed working.) + + # Second pass: clear light-off overrides so every geom node IS lit. + try: + nodes = root.findAllMatches('**/+GeomNode') + for i in range(nodes.getNumPaths()): + np = nodes.getPath(i) + try: + if np.getPythonTag(_LIGHTON_TAG): + continue + except Exception: + pass + try: + if _isFoliageOrTree(np): + try: + np.setLightOff(101) + except Exception: + pass + continue + # Same reasoning as _forceLightingOnSubtree: don't force lighting on + # billboarded geometry (camera-dependent normals/lighting artifacts). + try: + if np.hasEffect(BillboardEffect.getClassType()): + continue + except Exception: + pass + if hasattr(np, 'clearLightOff'): + np.clearLightOff() + try: + np.clearAttrib(LightAttrib.getClassType()) + except Exception: + pass + np.setPythonTag(_LIGHTON_TAG, True) + except Exception: + pass + except Exception: + pass + + +# ───────────────────────────────────────────────────────────────────────────── +# Street lamp / lantern night lights +# ───────────────────────────────────────────────────────────────────────────── + +def _scanForLampNodes(geom) -> list[NodePath]: + """Walk scene geometry and collect lamp/lantern NodePaths for night lights.""" + if geom is None or geom.isEmpty(): + return [] + results: list[NodePath] = [] + try: + nodes = geom.findAllMatches('**/*') + for i in range(nodes.getNumPaths()): + np = nodes.getPath(i) + name = np.getName().lower() + if any(k in name for k in _LAMP_KEYWORDS): + results.append(np) + if len(results) >= _LAMP_LIGHT_LIMIT: + break + except Exception: + pass + return results + + +def _enableLampLights(style: str) -> None: + """Create PointLights at all cached lamp positions (call at dusk/night).""" + global _lampLights, _lampLightsActive + if _lampLightsActive or not _lampGeomNps: + return + if not _lightRig or _lightRig.isEmpty(): + return + + # Zone-specific lamp colour (warm for regular areas, cool-blue for DL / midnight) + nightZones = ('dl', 'dl_street', 'sellbot_hq', 'cashbot_hq', 'lawbot_hq', + 'bossbot_hq', 'cog') + if style in nightZones: + # Cool blue-white sodium arc / fantasy lamp (night areas) + lampColor = Vec4(0.85, 0.92, 1.00, 1.0) + lampColor2 = Vec4(0.60, 0.75, 1.00, 1.0) # secondary soft fill + else: + # Warm yellow-orange street lamp (day/evening areas) + lampColor = Vec4(1.00, 0.85, 0.52, 1.0) + lampColor2 = Vec4(1.00, 0.70, 0.30, 1.0) # secondary warm glow + + for idx, lampNp in enumerate(_lampGeomNps): + try: + # Get world-space light-head position (try tight bounds top, else Z+offset) + try: + mins, maxs = lampNp.getTightBounds(base.render) + lightPos = Vec3( + (mins[0] + maxs[0]) * 0.5, + (mins[1] + maxs[1]) * 0.5, + maxs[2] + 0.5, # slightly above the top of the lamp geometry + ) + except Exception: + wp = lampNp.getPos(base.render) + lightPos = Vec3(wp.x, wp.y, wp.z + 4.5) + + pl = PointLight(f'outdoorLampLight_{idx}') + pl.setColor(lampColor) + # Realistic bright lamp: constant=0.1, linear=0, quadratic=0.005 + # This gives ~15 units of bright light and ~35 units of fall-off + # (quadratic 0.005 => radius where intensity = 0.5*peak is ~14 units) + pl.setAttenuation(Vec3(0.10, 0.0, 0.005)) + # Make shadows from lamp optional but enable bright illumination + try: + pl.setMaxDistance(45.0) + except Exception: + pass + + plNp = _lightRig.attachNewNode(pl) + plNp.setPos(base.render, lightPos) + base.render.setLight(plNp) + _lampLights.append(plNp) + + # Add a subtle secondary fill light slightly lower for volumetric feel + pl2 = PointLight(f'outdoorLampFill_{idx}') + pl2.setColor(Vec4(lampColor2[0]*0.4, lampColor2[1]*0.4, + lampColor2[2]*0.4, 1.0)) + pl2.setAttenuation(Vec3(0.20, 0.0, 0.012)) + plNp2 = _lightRig.attachNewNode(pl2) + plNp2.setPos(base.render, Vec3(lightPos.x, lightPos.y, lightPos.z - 1.8)) + base.render.setLight(plNp2) + _lampLights.append(plNp2) + except Exception: + pass + + _lampLightsActive = True + + +def _disableLampLights() -> None: + """Remove all street-lamp PointLights (call at sunrise/day).""" + global _lampLights, _lampLightsActive + for plNp in _lampLights: + try: + base.render.clearLight(plNp) + except Exception: + pass + try: + if not plNp.isEmpty(): + plNp.removeNode() + except Exception: + pass + _lampLights.clear() + _lampLightsActive = False + + +# ───────────────────────────────────────────────────────────────────────────── +# Day / night interpolation +# ───────────────────────────────────────────────────────────────────────────── + +def _evalKeyframes(zone: str, hour: float) -> dict: + frames = _DAY_NIGHT_KEYFRAMES.get(zone) + if not frames: + return {} + hour = hour % 24.0 + valid = [(h, d) for h, d in frames if d is not None] + if not valid: + return {} + if len(valid) == 1: + return dict(valid[0][1]) + + prev_h, prev_d = valid[-1] + next_h, next_d = valid[0] + for i in range(len(valid)): + kh, kd = valid[i] + if kh <= hour: + prev_h, prev_d = kh, kd + nxt_i = (i + 1) % len(valid) + next_h, next_d = valid[nxt_i] + + span = next_h - prev_h + if span <= 0: + span = (24.0 - prev_h) + next_h + t_prog = (hour - prev_h) % 24.0 + else: + t_prog = hour - prev_h + t = _clamp(t_prog / span if span > 0 else 0.0, 0.0, 1.0) + + result: dict = {} + all_keys = set(prev_d.keys()) | set(next_d.keys()) + for key in all_keys: + av = prev_d.get(key) + bv = next_d.get(key) + if isinstance(av, bool) or isinstance(bv, bool): + result[key] = bv if t >= 0.5 else av + elif isinstance(av, (int, float)) and isinstance(bv, (int, float)): + result[key] = _lerpF(float(av), float(bv), t) + elif isinstance(av, (tuple, list)) or isinstance(bv, (tuple, list)): + result[key] = _lerpT( + tuple(av) if av is not None else None, + tuple(bv) if bv is not None else None, + t, + ) + else: + result[key] = bv if t >= 0.5 else av + return result + + +def _getActiveSpec() -> dict: + base_spec = _ZONE_PROFILES.get(_activeStyle, _ZONE_PROFILES['playground']) + if not _wantDayNight() or not base_spec.get('dayNightEnabled', True): + return base_spec + overrides = _evalKeyframes(_activeStyle, _timeOfDay) + if not overrides: + return base_spec + merged = dict(base_spec) + merged.update(overrides) + return merged + + +# ───────────────────────────────────────────────────────────────────────────── +# Sky helpers +# ───────────────────────────────────────────────────────────────────────────── + +def _getHood(): + hood = getattr(getattr(base, 'cr', None), 'playGame', None) + return getattr(hood, 'hood', None) if hood else None + + +def _dimSkyForLights() -> None: + global _skyWasDimmed + hood = _getHood() + if _proceduralSky and _proceduralSky.isActive(): + # Procedural sky manages its own lighting — hide the model sky. + if hood and getattr(hood, 'sky', None) and not hood.sky.isEmpty(): + hood.sky.hide() + _skyWasDimmed = False + return + if hood and getattr(hood, 'sky', None) and not hood.sky.isEmpty(): + hood.sky.setLightOff(1) + _skyWasDimmed = True + + +def _restoreSkyLighting() -> None: + global _skyWasDimmed + if not _skyWasDimmed: + return + hood = _getHood() + if hood and getattr(hood, 'sky', None) and not hood.sky.isEmpty(): + if hasattr(hood.sky, 'clearLightOff'): + hood.sky.clearLightOff() + else: + try: + hood.sky.clearAttrib(LightAttrib.getClassType()) + except Exception: + try: + hood.sky.setLightOff(0) + except Exception: + pass + hood.sky.show() + _skyWasDimmed = False + + +def _tintSky(spec: dict) -> None: + if _proceduralSky and _proceduralSky.isActive(): + return # Sky handled by procedural system + hood = _getHood() + if hood and getattr(hood, 'sky', None) and not hood.sky.isEmpty(): + sc = spec.get('skyScale', (1, 1, 1, 1)) + hood.sky.setColorScale(Vec4(*sc)) + + +def _clearSkyTint() -> None: + hood = _getHood() + if hood and getattr(hood, 'sky', None) and not hood.sky.isEmpty(): + hood.sky.clearColorScale() + hood.sky.show() + + +# ───────────────────────────────────────────────────────────────────────────── +# Window background colour +# ───────────────────────────────────────────────────────────────────────────── + +def _applyBackgroundColor(spec: dict) -> None: + global _prevClearColor + try: + cc = spec.get('clearColor', (0.2, 0.4, 0.6, 1.0)) + if hasattr(base, 'win') and base.win: + if _prevClearColor is None: + _prevClearColor = tuple(base.win.getClearColor()) + base.win.setClearColor(Vec4(*cc)) + except Exception: + pass + + +def _restoreBackgroundColor() -> None: + global _prevClearColor + try: + if _prevClearColor and hasattr(base, 'win') and base.win: + base.win.setClearColor(Vec4(*_prevClearColor)) + except Exception: + pass + _prevClearColor = None + + +# ───────────────────────────────────────────────────────────────────────────── +# Shadow / scene bounds +# ───────────────────────────────────────────────────────────────────────────── + +def _computeFullZoneShadowBounds(geom, defaultArea: float) -> tuple[Vec3, float, float]: + focus = Vec3(0.0, 0.0, 0.0) + half = max(100.0, float(defaultArea)) + if geom is None or geom.isEmpty(): + return (focus, half, half) + try: + bounds = geom.getTightBounds() + if bounds and len(bounds) == 2: + mins, maxs = bounds + center = (mins + maxs) * 0.5 + size = maxs - mins + focus = Vec3(center[0], center[1], center[2]) + half = max(half, max(float(size[0]), float(size[1])) * 0.55) + except Exception: + pass + return (focus, half * 0.8, half) + + +# ───────────────────────────────────────────────────────────────────────────── +# Light rig +# ───────────────────────────────────────────────────────────────────────────── + +def _positionShadowCaster() -> None: + if not (_keyCastsShadows and _keyLightNp and not _keyLightNp.isEmpty() + and _shadowFocusPos is not None): + return + try: + # Keep the shadow frustum centered near the camera so it doesn't "pop" + # on/off as the player turns around (looking at areas outside the film + # size centered on a fixed zone origin). + target = Vec3(_shadowFocusPos[0], _shadowFocusPos[1], _shadowFocusPos[2]) + try: + cam_np = getattr(base, 'camera', None) + if cam_np is not None and not cam_np.isEmpty(): + cam_pos = cam_np.getPos(base.render) + # Follow camera in XY, but clamp so we don't chase too far and + # lose overall zone coverage. + dx = float(cam_pos[0] - target[0]) + dy = float(cam_pos[1] - target[1]) + # Maximum offset from the zone focus we allow the shadow center. + max_off = max(40.0, float(_shadowSceneRadius) * 0.60) + d2 = dx * dx + dy * dy + if d2 > (max_off * max_off): + inv_len = max_off / math.sqrt(d2) + dx *= inv_len + dy *= inv_len + # Blend toward the camera so the frustum tracks view direction. + follow = 0.75 + target = Vec3( + target[0] + dx * follow, + target[1] + dy * follow, + target[2], + ) + except Exception: + pass + texel = max(0.25, _shadowFilmArea / max(1.0, float(_shadowMapRes))) + # Only snap X and Y to the texel grid to prevent shadow edge swimming. + # Snapping Z too causes the shadow frustum to stutter vertically, which + # manifests as sharp spike-like seam artifacts along geometry silhouettes. + target = Vec3( + round(target[0] / texel) * texel, + round(target[1] / texel) * texel, + target[2], # Z intentionally not snapped – vertical snap = spike artifacts + ) + sunDir = None + try: + sunDir = Vec3(_sunDirWorld) if _sunDirWorld is not None else None + except Exception: + sunDir = None + if sunDir is None: + sunDir = _keyLightNp.getQuat(base.render).getForward() + shadowCamPos = target - sunDir * _shadowFollowDist + _keyLightNp.setPos(base.render, shadowCamPos) + except Exception: + pass + + +def _spawnLightRig(spec: dict, geom=None, + shadowBounds: tuple[Vec3, float] | None = None, + enable_ambient: bool = True, + enable_key: bool = True, + enable_fill: bool = True, + enable_rim: bool = True, + enable_shadows: bool = True) -> None: + global _lightRig, _renderLights, _keyLightNp, _shadowFollowDist + global _keyCastsShadows, _shadowFocusPos, _shadowSceneRadius + global _shadowMapRes, _shadowFilmArea, _prevShadowCasterState, _sunDirWorld + + intensity = _intensityScale() + _lightRig = base.render.attachNewNode('outdoorLightRig') + _renderLights = [] + _keyLightNp = None + _keyCastsShadows = False + _shadowFocusPos = None + _shadowSceneRadius = 180.0 + _shadowMapRes = 1024 + _shadowFilmArea = 180.0 + _shadowFollowDist = float(spec.get('shadowFollowDist', 220.0)) + defaultArea = float(spec.get('shadowArea', 180)) + + if shadowBounds is not None: + _shadowFocusPos = Vec3(*shadowBounds[0]) + _shadowSceneRadius = max(60.0, float(shadowBounds[1])) + fullHalf = float(shadowBounds[1]) + else: + focus, radius, fullHalf = _computeFullZoneShadowBounds(geom, defaultArea) + _shadowFocusPos = focus + _shadowSceneRadius = radius + try: + _dbg(f"shadowBounds focus={_shadowFocusPos} radius={_shadowSceneRadius} fullHalf={fullHalf}") + except Exception: + pass + + # ── Ambient ────────────────────────────────────────────────────────────── + if enable_ambient: + floor = float(spec.get('shadowDarknessFloor', 0.18)) + rawAmb = _applyColorTemp(_scaleColor(spec['ambient'], intensity)) + # Raise the floor slightly so ground planes are never pitch-black. + # We use a stronger floor multiplier on all channels so shadows + # don't swallow the ground geometry. + ambColor = ( + max(rawAmb[0], floor * 1.0), + max(rawAmb[1], floor * 0.90), + max(rawAmb[2], floor * 1.1), + rawAmb[3], + ) + amb = AmbientLight('outdoorAmbient') + amb.setColor(Vec4(*ambColor)) + ambNp = _lightRig.attachNewNode(amb) + # Apply lights both globally AND to the zone root. + # Critically, apply with priority 100 so zone-level clearLightOff + # calls can't accidentally block this ambient. + base.render.setLight(ambNp, 100) + try: + if geom is not None and not geom.isEmpty(): + geom.setLight(ambNp, 100) + except Exception: + pass + _renderLights.append(ambNp) + + # ── Key / sun ───────────────────────────────────────────────────────────── + if not enable_key: + return + keyColor = _applyColorTemp(_scaleColor(spec['key'], intensity)) + key = DirectionalLight('outdoorKey') + key.setColor(Vec4(*keyColor)) + wants_shadow = (enable_shadows + and spec.get('shadowCaster', False) + and _wantDynamicShadows() + and _shadowQuality() != 'off') + _prevShadowCasterState = wants_shadow + + if wants_shadow: + try: + res = _scaledShadowRes(spec) + # Clamp requested shadow resolution to GPU limits. + try: + gsg = base.win.getGsg() if getattr(base, 'win', None) else None + if gsg: + try: + max_dim = int(gsg.getMaxTextureDimension()) + except Exception: + max_dim = 0 + # Depth textures / shadow maps often fail silently if too large. + if max_dim and res > max_dim: + _dbg(f"clamping shadowRes {res} -> {max_dim} (GPU max texture dim)") + res = max_dim + # Snap down to a sane power-of-two bucket. + if res >= 4096: + res = 4096 + elif res >= 2048: + res = 2048 + elif res >= 1024: + res = 1024 + else: + res = 512 + try: + _dbg( + "gsg caps " + f"supportsFBO={gsg.getSupportsFramebuffer()} " + f"supportsBasicShaders={gsg.getSupportsBasicShaders()} " + f"supportsDepthTex={getattr(gsg, 'getSupportsDepthTexture', lambda: 'n/a')()} " + f"maxTexDim={gsg.getMaxTextureDimension()}" + ) + except Exception: + pass + except Exception: + pass + # Try to create shadow caster with error handling + try: + print(f"[DEBUG VideoSettings] _spawnLightRig: calling key.setShadowCaster(True, {res}, {res})...") + key.setShadowCaster(True, res, res) + print(f"[DEBUG VideoSettings] _spawnLightRig: key.setShadowCaster(True, {res}, {res}) returned.") + try: + is_caster = getattr(key, 'isShadowCaster', lambda: 'n/a')() + print(f"[DEBUG VideoSettings] _spawnLightRig: key.isShadowCaster={is_caster}") + except Exception: + pass + _shadowMapRes = res + _keyCastsShadows = True + try: + key.setCameraMask(_SHADOW_CAM_MASK) + except Exception: + pass + except Exception as e: + print(f"[DEBUG VideoSettings] _spawnLightRig: setShadowCaster({res}) failed with exception: {e!r}") + _keyCastsShadows = False + return # Skip the rest of shadow setup + + # Integer depth bias only. The 3-arg DepthOffsetAttrib.make(off, a, b) is + # NOT (offset, slope, maxBias) — it clamps projected Z to [min_value, max_value] + # in *normalized* depth, both in [0, 1]. Passing 2.5 triggers assertions and + # breaks shadow maps (see logs: depthOffsetAttrib.cxx min_value 0..1 check). + # A too-large depth offset bias can cause *everything* to evaluate as + # "in shadow" (overly dark world) depending on driver/build. Keep the + # default conservative and allow per-zone override. + depthOffset = int(spec.get('shadowDepthOffset', 2)) + depthOffset = max(0, min(8, depthOffset)) + key.setInitialState(key.getInitialState().addAttrib(DepthOffsetAttrib.make(depthOffset))) + + # Soften blocky shadow edges where supported by the build by setting + # shadow map texture filtering to linear. + try: + sm = None + if hasattr(key, 'getShadowMap'): + try: + sm = key.getShadowMap(0) + except Exception: + sm = None + if sm: + try: + sm.setMinfilter(Texture.FTShadow) + sm.setMagfilter(Texture.FTShadow) + except Exception: + pass + except Exception: + pass + + areaScale = float(spec.get('shadowAreaScale', 1.0)) + area = fullHalf * 2.0 * _clamp(areaScale, 0.75, 2.00) + maxScale = float(spec.get('shadowMaxAreaScale', 3.0)) + area = _clamp(area, defaultArea, + defaultArea * _clamp(maxScale, 1.5, 4.0)) + key.getLens().setFilmSize(area, area) + _shadowFilmArea = float(area) + + nearClip = max(4.0, _shadowFollowDist - (_shadowSceneRadius * 1.35)) + farClip = (_shadowFollowDist + (_shadowSceneRadius * 1.85) + + max(80.0, area * 0.30)) + if farClip <= nearClip + 32.0: + farClip = nearClip + 32.0 + key.getLens().setNearFar(float(nearClip), float(farClip)) + _keyCastsShadows = True + try: + _dbg(f"shadowLens film={key.getLens().getFilmSize()} nearFar={key.getLens().getNearFar()}") + except Exception: + pass + + # NOTE: + # This "force huge frustum" debug knob makes shadow texel density so low + # that it can look like blocky square lighting across the whole scene. + # Do NOT enable it just because lighting-debug is on. + try: + if ConfigVariableBool('lighting-shadow-force-huge-frustum', False).value: + key.getLens().setFilmSize(max(2000.0, float(area)), max(2000.0, float(area))) + key.getLens().setNearFar(1.0, 20000.0) + _dbg(f"shadowLens(huge frustum) film={key.getLens().getFilmSize()} nearFar={key.getLens().getNearFar()}") + _shadowFilmArea = max(2000.0, float(area)) + except Exception: + pass + try: + # Some builds expose shadow buffer/texture accessors. + for attr in ('getNumShadowMaps', 'getNumShadowBuffers'): + if hasattr(key, attr): + _dbg(f"key.{attr}()={getattr(key, attr)()}") + for attr in ('getShadowBuffer', 'getShadowMap'): + if hasattr(key, attr): + try: + v = getattr(key, attr)(0) + _dbg(f"key.{attr}(0)={'ok' if v else 'none'}") + except Exception: + pass + except Exception: + pass + except Exception as outer_e: + print(f"[DEBUG VideoSettings] _spawnLightRig: outer exception in primary shadow setup: {outer_e!r}. Attempting fallback 1024 shadow setup...") + try: + print("[DEBUG VideoSettings] _spawnLightRig: calling fallback key.setShadowCaster(True, 1024, 1024)...") + key.setShadowCaster(True, 1024, 1024) + print("[DEBUG VideoSettings] _spawnLightRig: fallback key.setShadowCaster returned.") + try: + key.setCameraMask(_SHADOW_CAM_MASK) + except Exception: + pass + _shadowMapRes = 1024 + key.setInitialState(key.getInitialState().addAttrib(DepthOffsetAttrib.make(4))) + fallArea = float(spec.get('shadowArea', 180)) + key.getLens().setFilmSize(fallArea, fallArea) + key.getLens().setNearFar(10, int(_shadowFollowDist * 2.0 + 500)) + _shadowFilmArea = fallArea + _keyCastsShadows = True + except Exception as fallback_e: + print(f"[DEBUG VideoSettings] _spawnLightRig: fallback setShadowCaster failed: {fallback_e!r}") + + # Compute and set the directional light vector explicitly. + # In some Panda3D builds, the DirectionalLight's printed direction and the + # shader generator use the node's internal direction (not the NodePath's HPR). + # If we only rotate the NodePath, the light can remain at the default (0,1,0), + # producing "sunlight only on edges" and a dark world. + try: + kh, kp, kr = spec.get('keyHpr', (0, -45, 0)) + _sunDirWorld = _sunDirFromHpr((kh, kp, kr)) + key.setDirection(_sunDirWorld) + except Exception: + _sunDirWorld = Vec3(0, 0, -1) + try: + key.setDirection(_sunDirWorld) + except Exception: + pass + + keyNp = _lightRig.attachNewNode(key) + # Also set the NodePath HPR so the shadow camera uses a consistent basis. + try: + keyNp.setHpr(float(kh), float(kp), float(kr)) + except Exception: + pass + base.render.setLight(keyNp, 100) + try: + if geom is not None and not geom.isEmpty(): + geom.setLight(keyNp, 100) + except Exception: + pass + _renderLights.append(keyNp) + _keyLightNp = keyNp + _positionShadowCaster() + + # ── Fill ────────────────────────────────────────────────────────────────── + if enable_fill: + fillColor = _applyColorTemp(_scaleColor(spec['fill'], intensity * 0.90)) + # IMPORTANT: + # A *directional* fill light is view-independent in theory, but in practice + # Toontown content contains camera-facing cards / special effects / odd + # transforms that make directional multi-light shading show camera-dependent + # RGB artifacts (your "RGB vertical surfaces" issue). + # + # We therefore implement fill as a *soft ambient* contribution. This preserves + # the overall mood and lifts shadows without introducing view-dependent hue + # banding, and also avoids the TransformState/normal-matrix singular crashes + # observed when adding extra directionals. + # Increased contribution from 0.70 to 0.80 for a brighter scene. + fillColor = _clampChromaRGBA(_desaturateRGBA(fillColor, 0.45), 0.45) + fill = AmbientLight('outdoorFillAmbient') + fill.setColor(Vec4(fillColor[0] * 0.80, fillColor[1] * 0.80, fillColor[2] * 0.80, fillColor[3])) + fillNp = _lightRig.attachNewNode(fill) + base.render.setLight(fillNp, 100) + try: + if geom is not None and not geom.isEmpty(): + geom.setLight(fillNp, 100) + except Exception: + pass + _renderLights.append(fillNp) + + # ── Rim ─────────────────────────────────────────────────────────────────── + if enable_rim and spec.get('rim'): + rimColor = _applyColorTemp(_scaleColor(spec['rim'], intensity * 0.75)) + # Same reasoning as fill: keep rim as a subtle ambient tint to avoid + # view-dependent color banding on flat/camera-facing geometry. + # Increased contribution from 0.22 to 0.32 for more visible rim light. + rimColor = _clampChromaRGBA(_desaturateRGBA(rimColor, 0.45), 0.45) + rim = AmbientLight('outdoorRimAmbient') + rim.setColor(Vec4(rimColor[0] * 0.32, rimColor[1] * 0.32, rimColor[2] * 0.32, rimColor[3])) + rimNp = _lightRig.attachNewNode(rim) + base.render.setLight(rimNp, 100) + try: + if geom is not None and not geom.isEmpty(): + geom.setLight(rimNp, 100) + except Exception: + pass + _renderLights.append(rimNp) + + +def _destroyLightRig() -> None: + global _lightRig, _renderLights, _keyLightNp, _keyCastsShadows + global _shadowFocusPos, _shadowSceneRadius, _shadowMapRes, _shadowFilmArea + # NOTE: _prevShadowCasterState is intentionally NOT reset here. + # Resetting it to None causes _applyProfileLive (day/night tick) to + # immediately detect a state change (True != None) and call + # _scheduleRigRebuild, which triggers a second setShadowCaster() call + # while the first shadow FBO is still live in the render thread. + # That double-FBO creation causes a GL context deadlock / permanent freeze. + _disableLampLights() + if _keyLightNp and not _keyLightNp.isEmpty(): + try: + k = _keyLightNp.getNode(0) + if hasattr(k, 'setShadowCaster') and getattr(k, 'isShadowCaster', lambda: False)(): + print("[DEBUG VideoSettings] _destroyLightRig: explicitly disabling shadow caster on key light to free offscreen FBO...") + k.setShadowCaster(False) + print("[DEBUG VideoSettings] _destroyLightRig: key.setShadowCaster(False) returned.") + except Exception as e: + print(f"[DEBUG VideoSettings] _destroyLightRig: failed to disable shadow caster: {e!r}") + for ln in _renderLights: + try: + base.render.clearLight(ln) + except Exception: + pass + _renderLights = [] + _keyLightNp = None + _keyCastsShadows = False + _shadowFocusPos = None + _shadowSceneRadius = 180.0 + _shadowMapRes = 1024 + _shadowFilmArea = 180.0 + if _lightRig and not _lightRig.isEmpty(): + _lightRig.removeNode() + _lightRig = None + + +def _applyProfileLive(spec: dict) -> None: + print(f"[DEBUG VideoSettings] _applyProfileLive: called.") + if not (_lightRig and not _lightRig.isEmpty()): + print(f"[DEBUG VideoSettings] _applyProfileLive: lightRig is empty/None. Returning.") + return + + intensity = _intensityScale() + floor = float(spec.get('shadowDarknessFloor', 0.18)) + + for np in _renderLights: + node = np.getNode(0) + name = node.getName() + + if name == 'outdoorAmbient': + raw = _applyColorTemp(_scaleColor( + spec.get('ambient', (0.2, 0.2, 0.3, 1)), intensity)) + c = (max(raw[0], floor * 0.8), max(raw[1], floor * 0.7), + max(raw[2], floor * 0.9), raw[3]) + node.setColor(Vec4(*c)) + + elif name == 'outdoorKey': + c = _applyColorTemp(_scaleColor( + spec.get('key', (1, 1, 1, 1)), intensity)) + node.setColor(Vec4(*c)) + np.setHpr(*spec.get('keyHpr', (0, -45, 0))) + + elif name == 'outdoorFillAmbient': + raw = _applyColorTemp(_scaleColor( + spec.get('fill', (0.5, 0.5, 0.5, 1)), intensity * 0.90)) + fillColor = _clampChromaRGBA(_desaturateRGBA(raw, 0.45), 0.45) + node.setColor(Vec4(fillColor[0] * 0.80, fillColor[1] * 0.80, fillColor[2] * 0.80, fillColor[3])) + + elif name == 'outdoorRimAmbient': + rim = spec.get('rim') + if rim: + raw = _applyColorTemp(_scaleColor(rim, intensity * 0.75)) + rimColor = _clampChromaRGBA(_desaturateRGBA(raw, 0.45), 0.45) + node.setColor(Vec4(rimColor[0] * 0.32, rimColor[1] * 0.32, rimColor[2] * 0.32, rimColor[3])) + + # Fog live update + if _fogNode: + fc = spec.get('fogColor', (0.5, 0.6, 0.8, 1)) + exp = spec.get('fogExponent') + dm = _fogDensityMult() + _fogNode.setColor(Vec4(*fc)) + if exp is not None: + _fogNode.setExpDensity(exp * dm) + elif spec.get('fogFar'): + near_v = spec.get('fogNear', 80.0) + far_v = spec.get('fogFar', 450.0) + adjusted_near = near_v / dm + adjusted_far = far_v / dm + _fogNode.setLinearRange(adjusted_near, adjusted_far) + + _tintSky(spec) + _applyBackgroundColor(spec) + + # God rays overlay (render2dp) + if _godRaysCard and not _godRaysCard.isEmpty(): + rawI = spec.get('rayIntensity', 0.0) + eff = rawI * _intensityScale() + if _settingsBool('lighting-god-rays', True) and eff > 0.001: + _godRaysCard.setShaderInput('rayIntensity', eff) + _godRaysCard.setShaderInput('rayColor', + Vec4(*spec.get('rayColor', (1, 1, 1, 1)))) + _godRaysCard.show() + else: + _godRaysCard.hide() + + # Shadow-caster state transition + global _prevShadowCasterState + wants = (spec.get('shadowCaster', False) + and _wantDynamicShadows() + and _shadowQuality() != 'off') + print(f"[DEBUG VideoSettings] _applyProfileLive: wants shadow={wants}, _prevShadowCasterState={_prevShadowCasterState}") + # Only trigger a rebuild when the state has actually changed from a known + # prior value. If _prevShadowCasterState is None it means the rig was just + # destroyed and _spawnLightRig hasn't run yet - skip the rebuild to avoid + # a double setShadowCaster() / double FBO creation deadlock. + if _prevShadowCasterState is not None and wants != _prevShadowCasterState: + print(f"[DEBUG VideoSettings] _applyProfileLive: shadow caster state mismatch! Scheduling rig rebuild...") + _scheduleRigRebuild(spec) + print(f"[DEBUG VideoSettings] _applyProfileLive: complete.") + + +def _scheduleRigRebuild(spec: dict) -> None: + global _rigRebuildPending + print(f"[DEBUG VideoSettings] _scheduleRigRebuild: scheduling rig rebuild.") + _rigRebuildPending = spec + + +def _flushRigRebuild() -> None: + global _rigRebuildPending, _activeGeom + if _rigRebuildPending is None: + return + print(f"[DEBUG VideoSettings] _flushRigRebuild: starting pending rebuild...") + # During feature bisect, avoid rebuilding/spawning the light rig on steps + # that are meant to have no lights (eg step 1 = shader-auto only). + try: + if not _bisectAllows(2): + print(f"[DEBUG VideoSettings] _flushRigRebuild: bisect step does not allow lights. Cancelling rebuild.") + _rigRebuildPending = None + return + except Exception: + pass + spec = _rigRebuildPending + _rigRebuildPending = None + savedBounds = None + if _shadowFocusPos is not None: + savedBounds = (Vec3(_shadowFocusPos), float(_shadowSceneRadius)) + print(f"[DEBUG VideoSettings] _flushRigRebuild: destroying old light rig...") + _destroyLightRig() + print(f"[DEBUG VideoSettings] _flushRigRebuild: spawning new light rig...") + _spawnLightRig(spec, geom=_activeGeom, shadowBounds=savedBounds) + if _activeGeom is not None and not _activeGeom.isEmpty(): + try: + _activeGeom.setShaderAuto() + _forceShadersOnSubtree(_activeGeom) + _forceShaderRegen(_activeGeom) + _forceLightingOnSubtree(_activeGeom) + _hidePlaneLikeCastersFromShadow(_activeGeom) + except Exception as e: + print(f"[DEBUG VideoSettings] _flushRigRebuild: geom shader regen exception: {e!r}") + lav = getattr(base, 'localAvatar', None) + if lav is not None and not lav.isEmpty(): + try: + _forceShaderRegen(lav) + except Exception as e: + print(f"[DEBUG VideoSettings] _flushRigRebuild: localAvatar shader regen exception: {e!r}") + print(f"[DEBUG VideoSettings] _flushRigRebuild: complete.") + + +# ───────────────────────────────────────────────────────────────────────────── +# Atmospheric fog +# ───────────────────────────────────────────────────────────────────────────── + +def _applyFog(spec: dict) -> None: + global _fogNode + if not _bisectAllows(5): + return + if not _settingsBool('lighting-fog-enabled', True): + return + fogColor = spec.get('fogColor') + if not fogColor: + return + dm = _fogDensityMult() + _fogNode = Fog('outdoorFog') + _fogNode.setColor(Vec4(*fogColor)) + exponent = spec.get('fogExponent') + if exponent is not None: + _fogNode.setExpDensity(exponent * dm) + else: + near_v = spec.get('fogNear', 80.0) + far_v = spec.get('fogFar', 450.0) + _fogNode.setLinearRange(near_v / dm, far_v / dm) + base.render.setFog(_fogNode) + + +def _clearFog() -> None: + global _fogNode + try: + base.render.clearFog() + except Exception: + pass + _fogNode = None + + +# ───────────────────────────────────────────────────────────────────────────── +# Post-process (bloom + 2D sun-ray overlay — no custom scene→texture compositor) +# ───────────────────────────────────────────────────────────────────────────── + +def _maintainOutdoorLightingViewport() -> None: + """Keep the main window's DisplayRegions at full size and pixel_zoom=1. + + Two separate issues produce the same “3D in the bottom-left quarter” look: + (1) per-DisplayRegion **pixel_zoom** > 1, or (2) normalized **dimensions** + smaller than the full window on the compositor / 3D region. Stock + FilterManager does not always keep those in sync on every GL driver. + + When outdoor FX is active we therefore reset **every** main-window + DisplayRegion to (0,1)×(0,1) and pixel_zoom 1 each frame. That can break + rare sub-rectangle DR tricks (e.g. picture-in-picture) while the rig runs. + + Each operation is individually guarded so that a threading assertion on + win.setPixelZoom (observed on the custom Panda3D build) cannot abort the + entire repair and leave the DR dimensions uncorrected. + """ + win = getattr(base, 'win', None) + if not win: + return + + # Window-level pixel zoom — may raise assertion on some builds; keep isolated. + try: + win.setPixelZoom(1) + except Exception: + pass + + # Per-DR dimensions and pixel zoom. + try: + n = win.getNumDisplayRegions() + except Exception: + n = 0 + for i in range(n): + try: + dr = win.getDisplayRegion(i) + if not dr: + continue + dr.setDimensions(0.0, 1.0, 0.0, 1.0) + try: + if dr.supportsPixelZoom(): + dr.setPixelZoom(1) + except Exception: + pass + except Exception: + pass + + # RTT buffer DisplayRegions (scene capture buffers, bloom passes, etc.) + _fixPostProcessRttViewports() + + +def _setupCinematicPost(spec: dict) -> None: + """Bloom plus additive sun shafts — no full-window scene RTT compositor.""" + _setupBloom(spec) + if _bisectAllows(8): + _createGodRaysOverlay(spec) + + +def _setupPostProcess(spec: dict) -> None: + _destroyPostProcess() + + if not _wantFx(): + return + + mode = 'full' + if _OUTDOOR_SHADER_BISECT_LEVEL == 2: + mode = _LEVEL2_POST_SINGLE + if mode not in ('full', 'bloom', 'godrays', 'stack'): + mode = 'full' + + if mode == 'godrays': + if not _bisectAllows(8): + return + _createGodRaysOverlay(spec) + return + if mode == 'bloom': + _setupBloom(spec) + return + # 'full' and 'stack': bloom + god rays + _setupCinematicPost(spec) + + +def _destroyPostProcess() -> None: + _destroyBloom() + _destroyGodRaysOverlay() + + +# ───────────────────────────────────────────────────────────────────────────── +# Bloom (CommonFilters) +# ───────────────────────────────────────────────────────────────────────────── + +def _setupBloom(spec: dict) -> None: + global _bloomFilters + if not _wantBloom(): + return + intensity = float(spec.get('bloomIntensity', 0.0)) + if intensity <= 0.001: + return + try: + from direct.filter.CommonFilters import CommonFilters + _bloomFilters = CommonFilters(base.win, base.cam) + threshold = float(spec.get('bloomThreshold', 0.65)) + size = 'large' if intensity >= 0.55 else ('medium' if intensity >= 0.25 else 'small') + _bloomFilters.setBloom( + size=size, + intensity=intensity, + mintrigger=threshold, + maxtrigger=min(1.0, threshold + 0.30), + desat=-0.4, + ) + # CommonFilters uses the base FilterManager.renderSceneInto which calls + # buffer.makeDisplayRegion() without explicit bounds. On some GL drivers + # (including the custom OpenToontown build) that defaults to a bottom-left + # sub-rectangle, causing the scene to render into only part of the colour + # texture — the root cause of the "3D view in the bottom-left" bug. + # Force full-window bounds on every offscreen DR immediately after setup. + _fixPostProcessRttViewports() + except Exception: + _bloomFilters = None + + +def _destroyBloom() -> None: + global _bloomFilters + if _bloomFilters is not None: + try: + _bloomFilters.cleanup() + except Exception: + pass + _bloomFilters = None + + +def _updateBloomLive(spec: dict) -> None: + if _bloomFilters is None: + return + try: + intensity = float(spec.get('bloomIntensity', 0.0)) * _intensityScale() + if intensity <= 0.001: + _bloomFilters.delBloom() + return + threshold = float(spec.get('bloomThreshold', 0.65)) + size = 'large' if intensity >= 0.55 else ('medium' if intensity >= 0.25 else 'small') + _bloomFilters.setBloom( + size=size, intensity=intensity, + mintrigger=threshold, maxtrigger=min(1.0, threshold + 0.30), + desat=-0.4, + ) + except Exception: + pass + + +# ───────────────────────────────────────────────────────────────────────────── +# Sun-ray overlay (render2dp) +# ───────────────────────────────────────────────────────────────────────────── + +def _loadGodRaysShader() -> Shader | None: + global _godRaysShader + if _godRaysShader is not None: + return _godRaysShader + try: + vp_os = os.path.normpath(_LEGACY_VERT) + fp_os = os.path.normpath(_LEGACY_FRAG) + if not (os.path.isfile(vp_os) and os.path.isfile(fp_os)): + return None + vp = Filename.fromOsSpecific(vp_os) + fp = Filename.fromOsSpecific(fp_os) + try: + vp.makeTrueCase() + fp.makeTrueCase() + except Exception: + pass + _godRaysShader = Shader.load(Shader.SL_GLSL, vp, fp) + return _godRaysShader + except Exception: + return None + + +def _createGodRaysOverlay(spec: dict) -> None: + global _godRaysCard + if not _bisectAllows(8): + return + if not _settingsBool('lighting-god-rays', True): + return + rawI = spec.get('rayIntensity', 0.0) + if rawI <= 0.0: + return + shader = _loadGodRaysShader() + if shader is None: + return + try: + cm = CardMaker('outdoorGodRays') + cm.setFrameFullscreenQuad() + card = base.render2dp.attachNewNode(cm.generate()) + card.setDepthTest(False) + card.setDepthWrite(False) + card.setTransparency(TransparencyAttrib.MAlpha) + card.setAttrib(ColorBlendAttrib.make( + ColorBlendAttrib.MAdd, + ColorBlendAttrib.OIncomingAlpha, + ColorBlendAttrib.OOne, + )) + card.setShader(shader) + + su, sv = spec.get('sunUV', (0.5, 0.75)) + rc = spec.get('rayColor', (1.0, 1.0, 1.0, 1.0)) + # Many zone profiles use very warm/orange rayColor which can turn + # blown-out highlights into "red sunlight". Prefer a neutral color that + # matches the actual key light. + try: + if _keyLightNp is not None and not _keyLightNp.isEmpty(): + k = _keyLightNp.getNode(0) + if k and hasattr(k, 'getColor'): + kc = k.getColor() + # Clamp and slightly desaturate toward white. + r = max(0.0, min(1.0, float(kc[0]))) + g = max(0.0, min(1.0, float(kc[1]))) + b = max(0.0, min(1.0, float(kc[2]))) + desat = 0.55 + lum = (r + g + b) / 3.0 + r = r * (1.0 - desat) + lum * desat + g = g * (1.0 - desat) + lum * desat + b = b * (1.0 - desat) + lum * desat + rc = (r, g, b, float(rc[3]) if len(rc) >= 4 else 1.0) + except Exception: + pass + eff = rawI * _intensityScale() + card.setShaderInput('sunPos', (su, sv)) + card.setShaderInput('rayColor', Vec4(*rc)) + card.setShaderInput('rayIntensity', eff) + card.setShaderInput('time', 0.0) + try: + props = base.win.getProperties() + ar = props.getXSize() / max(1, props.getYSize()) + except Exception: + ar = 1.333 + card.setShaderInput('aspectRatio', ar) + _godRaysCard = card + except Exception: + _godRaysCard = None + + +def _destroyGodRaysOverlay() -> None: + global _godRaysCard + if _godRaysCard and not _godRaysCard.isEmpty(): + _godRaysCard.removeNode() + _godRaysCard = None + + +# ───────────────────────────────────────────────────────────────────────────── +# Procedural sky +# ───────────────────────────────────────────────────────────────────────────── + +def _setupProceduralSky(spec: dict) -> None: + global _proceduralSky + if not _wantProceduralSky(): + return + try: + try: + from toontown.hood.ProceduralSky import ProceduralSky + except ImportError: + from ProceduralSky import ProceduralSky + _proceduralSky = ProceduralSky() + # Parent to the lens NodePath (base.cam) so the dome shares the same + # transform as the camera FilterManager renders with; base.camera can sit + # slightly off the lens on third-person rigs. + _skyParent = base.cam if (getattr(base, 'cam', None) and not base.cam.isEmpty()) else base.camera + _proceduralSky.attach(_skyParent, style=_activeStyle) + if _proceduralSky and _proceduralSky.isActive(): + try: + _proceduralSky._skyNp.hide(_SHADOW_CAM_MASK) + except Exception: + pass + _proceduralSky.update(spec, _timeOfDay) + hood = _getHood() + if hood and getattr(hood, 'sky', None) and not hood.sky.isEmpty(): + # If the procedural sky failed to attach (shader missing, etc), + # keep the legacy model sky visible as a fallback. + if _proceduralSky and _proceduralSky.isActive(): + hood.sky.hide(BitMask32.allOn()) + else: + hood.sky.show(BitMask32.allOn()) + except Exception as e: + import traceback + print(f"[DEBUG VideoSettings] _setupProceduralSky exception: {e!r}") + traceback.print_exc() + _proceduralSky = None + + +def _destroyProceduralSky() -> None: + global _proceduralSky + if _proceduralSky is not None: + try: + _proceduralSky.detach() + except Exception: + pass + _proceduralSky = None + + +# ───────────────────────────────────────────────────────────────────────────── +# Water reflection system (unchanged from previous version) +# ───────────────────────────────────────────────────────────────────────────── + +def _loadWaterShader() -> Shader | None: + global _waterShader + if _waterShader is not None: + return _waterShader + try: + vp_os = os.path.normpath(_WATER_VERT) + fp_os = os.path.normpath(_WATER_FRAG) + if not (os.path.isfile(vp_os) and os.path.isfile(fp_os)): + return None + vp = Filename.fromOsSpecific(vp_os) + fp = Filename.fromOsSpecific(fp_os) + try: + vp.makeTrueCase() + fp.makeTrueCase() + except Exception: + pass + _waterShader = Shader.load(Shader.SL_GLSL, vp, fp) + return _waterShader + except Exception: + return None + + +def _isWaterNode(name: str) -> bool: + nl = name.lower() + return any(p in nl for p in _WATER_PATTERNS) + + +def _findWaterNodes(geom) -> list[NodePath]: + if geom is None or geom.isEmpty(): + return [] + results: list[NodePath] = [] + try: + all_nodes = geom.findAllMatches('**/*') + for i in range(all_nodes.getNumPaths()): + np = all_nodes.getPath(i) + if _isWaterNode(np.getName()): + results.append(np) + except Exception: + pass + return results + + +def _setupWaterNode(waterNp: NodePath, spec: dict) -> dict | None: + if not _wantWater(): + return None + shader = _loadWaterShader() + if shader is None: + wc = spec.get('waterColor', (0.22, 0.38, 0.55, 0.85)) + try: + waterNp.setColorScale(Vec4(*wc)) + except Exception: + pass + return None + + quality = spec.get('waterReflQuality', 'medium') + bufW, bufH = {'high': (1024, 1024), 'low': (256, 256)}.get(quality, (512, 512)) + + try: + buf = base.win.makeTextureBuffer('waterRefl', bufW, bufH) + buf.setClearColor(Vec4(0.1, 0.2, 0.3, 1.0)) + reflTex = buf.getTexture() + reflCamNp = base.makeCamera(buf) + reflCamNp.reparentTo(base.render) + + waterHeight = waterNp.getZ(base.render) + taskName = f'waterReflTask_{id(waterNp)}' + + def _syncReflCam(task, rnp=reflCamNp, wh=waterHeight): + if rnp.isEmpty() or base.camera.isEmpty(): + return task.done + cp = base.camera.getPos(base.render) + ch = base.camera.getHpr(base.render) + mz = 2.0 * wh - cp.z + rnp.setPos(base.render, cp.x, cp.y, mz) + rnp.setHpr(base.render, ch.x, -ch.y, ch.z) + try: + rnp.node().getLens().setNearFar( + base.camNode.getLens().getNear(), + base.camNode.getLens().getFar(), + ) + except Exception: + pass + return task.cont + + taskMgr.add(_syncReflCam, taskName, sort=44) + + wc = spec.get('waterColor', (0.22, 0.38, 0.55, 0.85)) + sdir = Vec3(0, -1, -1) + if _keyLightNp and not _keyLightNp.isEmpty(): + try: + sdir = _keyLightNp.getQuat(base.render).getForward() + sdir.normalize() + except Exception: + pass + + waterNp.setShaderAuto(False) + waterNp.setShader(shader) + waterNp.setShaderInput('osl_ReflectionTex', reflTex) + waterNp.setShaderInput('osl_WaterColor', Vec4(*wc)) + waterNp.setShaderInput('osl_SunColor', Vec4(1.0, 0.95, 0.80, 1.0)) + waterNp.setShaderInput('osl_SunDir', sdir) + waterNp.setShaderInput('osl_CameraPos', base.camera.getPos(base.render)) + waterNp.setShaderInput('osl_Time', 0.0) + waterNp.setShaderInput('osl_WaveScale', 1.4) + waterNp.setShaderInput('osl_WaveSpeed', 0.8) + waterNp.setShaderInput('osl_FresnelPower', 3.5) + waterNp.setShaderInput('osl_Roughness', 0.35) + waterNp.setShaderInput('osl_ReflectionStrength', 0.72) + waterNp.setTransparency(TransparencyAttrib.MAlpha) + + return { + 'np': waterNp, + 'buffer': buf, + 'camera': reflCamNp, + 'taskName': taskName, + } + except Exception: + return None + + +def _setupAllWater(geom, spec: dict) -> None: + global _waterSetups + if not spec.get('hasWater', False) or not _wantWater(): + return + for np in _findWaterNodes(geom): + setup = _setupWaterNode(np, spec) + if setup: + _waterSetups.append(setup) + try: + np.hide(_SHADOW_CAM_MASK) + except Exception: + pass + + +def _cleanupAllWater() -> None: + global _waterSetups, _waterShader + for ws in _waterSetups: + try: + taskMgr.remove(ws['taskName']) + except Exception: + pass + try: + cam = ws.get('camera') + if cam and not cam.isEmpty(): + cam.removeNode() + except Exception: + pass + try: + buf = ws.get('buffer') + if buf: + base.graphicsEngine.removeWindow(buf) + except Exception: + pass + try: + np = ws.get('np') + if np and not np.isEmpty(): + np.clearShader() + np.clearColorScale() + except Exception: + pass + _waterSetups = [] + _waterShader = None + + +def _tickWaterUniforms(dt: float) -> None: + if not _waterSetups: + return + camPos = None + sunDir = Vec3(0, -1, -1) + try: + camPos = base.camera.getPos(base.render) + except Exception: + pass + if _sunDirWorld is not None: + try: + sunDir = Vec3(_sunDirWorld) + sunDir.normalize() + except Exception: + pass + elif _keyLightNp and not _keyLightNp.isEmpty(): + try: + sunDir = _keyLightNp.getQuat(base.render).getForward() + sunDir.normalize() + except Exception: + pass + for ws in _waterSetups: + np = ws.get('np') + if not np or np.isEmpty(): + continue + try: + np.setShaderInput('osl_Time', _godRaysTime) + if camPos: + np.setShaderInput('osl_CameraPos', camPos) + np.setShaderInput('osl_SunDir', sunDir) + except Exception: + pass + + +# ───────────────────────────────────────────────────────────────────────────── +# Per-frame update task +# ───────────────────────────────────────────────────────────────────────────── + +def _lightingUpdateTask(task): + global _godRaysTime, _timeOfDay, _dayNightAccum + + # Teleport/zone transitions can temporarily tear down the scene graph in ways + # that make TransformState matrices non-invertible, which can crash inside + # Panda3D when we query transforms. We should *not* permanently stop this task + # on a transient bad frame, because that can leave the shadow caster frozen + # and make the whole world look incorrectly shadowed. Instead, keep the task + # alive and just skip frames that hit assertions. + import sys as _sys_ol; _sys_ol.stderr.write('[CRASH-DIAG] _lightingUpdateTask: tick start\n'); _sys_ol.stderr.flush() + print(f"[DEBUG VideoSettings] _lightingUpdateTask: tick start.", flush=True) + try: + _syncBase() + if base is None or getattr(base, 'render', None) is None: + print(f"[DEBUG VideoSettings] _lightingUpdateTask: base or base.render is None, returning task.done.", flush=True) + return task.done + if _refCount <= 0 or _lightRig is None or (_lightRig and _lightRig.isEmpty()): + print(f"[DEBUG VideoSettings] _lightingUpdateTask: refCount={_refCount} or lightRig is None/empty, returning task.done.", flush=True) + return task.done + except Exception as e: + print(f"[DEBUG VideoSettings] _lightingUpdateTask: exception in initial checks: {e!r}, returning task.done.") + return task.done + + dt = globalClock.getDt() + _godRaysTime += dt + + if _refCount > 0: + _sys_ol.stderr.write('[CRASH-DIAG] _lightingUpdateTask: maintainViewport\n'); _sys_ol.stderr.flush() + print(f"[DEBUG VideoSettings] _lightingUpdateTask: calling _maintainOutdoorLightingViewport...", flush=True) + _maintainOutdoorLightingViewport() + print(f"[DEBUG VideoSettings] _lightingUpdateTask: _maintainOutdoorLightingViewport returned.", flush=True) + + # ── Shadow camera positioning ───────────────────────────────────────────── + try: + _sys_ol.stderr.write('[CRASH-DIAG] _lightingUpdateTask: positionShadowCaster\n'); _sys_ol.stderr.flush() + print(f"[DEBUG VideoSettings] _lightingUpdateTask: calling _positionShadowCaster...", flush=True) + _positionShadowCaster() + print(f"[DEBUG VideoSettings] _lightingUpdateTask: _positionShadowCaster returned.", flush=True) + except AssertionError as ae: + print(f"[DEBUG VideoSettings] _lightingUpdateTask: _positionShadowCaster assertion error: {ae!r}") + return task.cont + except Exception as e: + print(f"[DEBUG VideoSettings] _lightingUpdateTask: _positionShadowCaster exception: {e!r}") + return task.cont + + # ── Water uniforms ──────────────────────────────────────────────────────── + try: + _sys_ol.stderr.write('[CRASH-DIAG] _lightingUpdateTask: tickWaterUniforms\n'); _sys_ol.stderr.flush() + print(f"[DEBUG VideoSettings] _lightingUpdateTask: calling _tickWaterUniforms...", flush=True) + _tickWaterUniforms(dt) + print(f"[DEBUG VideoSettings] _lightingUpdateTask: _tickWaterUniforms returned.", flush=True) + except AssertionError as ae: + print(f"[DEBUG VideoSettings] _lightingUpdateTask: _tickWaterUniforms assertion error: {ae!r}") + return task.cont + except Exception as e: + print(f"[DEBUG VideoSettings] _lightingUpdateTask: _tickWaterUniforms exception: {e!r}") + pass + + # ── Procedural sky update ───────────────────────────────────────────────── + try: + _sys_ol.stderr.write('[CRASH-DIAG] _lightingUpdateTask: proceduralSky\n'); _sys_ol.stderr.flush() + if _proceduralSky and _proceduralSky.isActive(): + print(f"[DEBUG VideoSettings] _lightingUpdateTask: calling procedural sky update...", flush=True) + spec = _getActiveSpec() + _proceduralSky.update(spec, _timeOfDay) + print(f"[DEBUG VideoSettings] _lightingUpdateTask: procedural sky update returned.") + hood = _getHood() + if hood and getattr(hood, 'sky', None) and not hood.sky.isEmpty(): + hood.sky.hide() + except AssertionError as ae: + print(f"[DEBUG VideoSettings] _lightingUpdateTask: procedural sky update assertion error: {ae!r}") + return task.cont + except Exception as e: + print(f"[DEBUG VideoSettings] _lightingUpdateTask: procedural sky update exception: {e!r}") + pass + + # ── Day / night cycle ───────────────────────────────────────────────────── + _sys_ol.stderr.write('[CRASH-DIAG] _lightingUpdateTask: dayNight\n'); _sys_ol.stderr.flush() + if _wantDayNight() and _refCount > 0: + spec = _ZONE_PROFILES.get(_activeStyle, _ZONE_PROFILES['playground']) + if spec.get('dayNightEnabled', True): + _dayNightAccum += dt + if _dayNightAccum >= 0.25: # 4 Hz update rate + print(f"[DEBUG VideoSettings] _lightingUpdateTask: 4 Hz update triggered. _dayNightAccum={_dayNightAccum}, _timeOfDay={_timeOfDay}") + gameHours = (_dayNightAccum + * _dayNightSpeed + * _dayNightSpeedMultiplier()) + _timeOfDay = (_timeOfDay + gameHours) % 24.0 + _dayNightAccum = 0.0 + cur_spec = _getActiveSpec() + print(f"[DEBUG VideoSettings] _lightingUpdateTask: calling _applyProfileLive...") + _applyProfileLive(cur_spec) + print(f"[DEBUG VideoSettings] _lightingUpdateTask: calling _updateBloomLive...") + _updateBloomLive(cur_spec) + + # ── Street lamp lights: on at night, off during day ─────────────────────── + if _lampGeomNps and _lightRig and not _lightRig.isEmpty(): + try: + print(f"[DEBUG VideoSettings] _lightingUpdateTask: updating street lamp lights...") + cur_spec = _getActiveSpec() + kHpr = cur_spec.get('keyHpr', (135, -42, 0)) + # Derive sun elevation: the key-light pitch angle p (kHpr[1]) gives: + # sunDirWorld.z = sin(p_degrees) + # Toontown's daytime uses p ≈ -42 → sin(-42°) ≈ -0.67 → below 0 + # A more negative p means sun is pointing *more* downward from above. + # The pitch is from the directional light's perspective: a large negative + # pitch means the light shines steeply from above (midday). + # At sunset/rise the absolute pitch shrinks toward 0 → sunDirZ→0. + # At "nighttime" key configs (DL, 0:00 etc.), pitch is still negative + # but we need to detect dusk/night by checking if the sun is LOW. + # + # Lamps should turn on when: + # (a) This is a perpetual-night zone (DL, COG HQs), OR + # (b) The active keyframe has a low sun (pitch close to 0 or above horizon) + # + # For day/night zones the "night" keyframes set pitch close to 0 or even + # positive. Threshold: enable lamps when pitch > -18° (sin > -0.31). + perpetualNight = _activeStyle in ( + 'dl', 'dl_street', 'sellbot_hq', 'cashbot_hq', + 'lawbot_hq', 'bossbot_hq', 'cog', + ) + pitch_deg = float(kHpr[1]) + sunLow = pitch_deg > -18.0 # sun is near horizon or above = night/dusk + if perpetualNight or sunLow: + _enableLampLights(_activeStyle) + else: + _disableLampLights() + print(f"[DEBUG VideoSettings] _lightingUpdateTask: street lamp lights updated.") + except Exception as e: + print(f"[DEBUG VideoSettings] _lightingUpdateTask: street lamp lights exception: {e!r}") + pass + + # ── Sun-ray overlay shimmer ─────────────────────────────────────────────── + _sys_ol.stderr.write('[CRASH-DIAG] _lightingUpdateTask: godRays\n'); _sys_ol.stderr.flush() + if _godRaysCard and not _godRaysCard.isEmpty(): + _godRaysCard.setShaderInput('time', _godRaysTime) + + # ── Deferred rig rebuild ────────────────────────────────────────────────── + _sys_ol.stderr.write('[CRASH-DIAG] _lightingUpdateTask: flushRigRebuild\n'); _sys_ol.stderr.flush() + try: + if _rigRebuildPending is not None: + print(f"[DEBUG VideoSettings] _lightingUpdateTask: calling _flushRigRebuild...", flush=True) + _flushRigRebuild() + except AssertionError as ae: + print(f"[DEBUG VideoSettings] _lightingUpdateTask: _flushRigRebuild assertion error: {ae!r}") + return task.cont + except Exception as e: + print(f"[DEBUG VideoSettings] _lightingUpdateTask: _flushRigRebuild exception: {e!r}") + pass + + _sys_ol.stderr.write('[CRASH-DIAG] _lightingUpdateTask: tick end\n'); _sys_ol.stderr.flush() + print(f"[DEBUG VideoSettings] _lightingUpdateTask: tick end.", flush=True) + return task.cont + + +# ───────────────────────────────────────────────────────────────────────────── +# Public API +# ───────────────────────────────────────────────────────────────────────────── + +def begin(geom, style: str = 'playground', + hoodId: int | None = None, + zoneId: int | None = None) -> None: + """Activate the outdoor lighting rig for the given geometry. + + Parameters + ---------- + geom: + Scene-geometry NodePath; receives per-pixel auto-shading. + style: + Fallback profile key (``'playground'``, ``'estate'``, ``'cog'``, etc.). + hoodId: + ToontownGlobals hood constant – overrides *style* for zone lookup. + zoneId: + Specific zone constant (e.g. SillyStreet) – used for street-level + profiles. Takes priority over *hoodId*. + """ + _syncBase() + global _refCount, _activeStyle, _activeGeom + if not _wantFx(): + return + + _activeGeom = geom + + resolvedStyle = _resolveStyle(style, hoodId, zoneId) + spec = _ZONE_PROFILES[resolvedStyle] + _dbg(f"begin(refCount={_refCount}) style={style} resolved={resolvedStyle} hoodId={hoodId} zoneId={zoneId}") + _maybeBindBisectHotkeys() + # Keep OTP blob/drop shadows synced regardless of shader support. + if _OTPDropshadow: + try: + _OTPDropshadow.setGlobalDropShadowFlag(1 if _settingsBool('dynamic-shadows', True) else 0) + except Exception: + pass + try: + _OTPDropshadow.setGlobalDropShadowGrayLevel(float(_settingsFloat('drop-shadow-strength', 0.5))) + except Exception: + pass + + _dbg(f"settings wantFx={_wantFx()} dynShadows={_wantDynamicShadows()} shadowQuality={_shadowQuality()} wantBloom={_wantBloom()} wantProceduralSky={_wantProceduralSky()}") + + if _refCount == 0: + _dbgBisectState() + _activeStyle = resolvedStyle + cur_spec = _getActiveSpec() if spec.get('dayNightEnabled', True) else spec + + # Hard cleanup of optional subsystems when bisecting. + # This prevents leftovers from prior runs (or other systems) from making + # it *look* like everything is still enabled at low bisect steps. + try: + if not _bisectAllows(9): + _cleanupAllWater() + except Exception: + pass + try: + if not _bisectAllows(8): + _destroyGodRaysOverlay() + except Exception: + pass + try: + if not _bisectAllows(7): + _destroyPostProcess() + except Exception: + pass + try: + if not _bisectAllows(6): + _destroyProceduralSky() + except Exception: + pass + try: + if not _bisectAllows(5): + _clearFog() + _clearSkyTint() + _restoreBackgroundColor() + except Exception: + pass + + # Step 0: fully disable OutdoorLighting effects and exit early after cleanup. + if not _bisectAllows(1): + return + # Ensure the root scene graph participates in shader generation and lighting. + # Some Toontown-era content relies on fixed-function defaults that can + # disable per-pixel lighting/shadow receiving unless shader-auto is + # enabled high up the graph. + try: + if getattr(base, 'render', None) is not None: + base.render.setShaderAuto() + if hasattr(base.render, 'clearLightOff'): + base.render.clearLightOff() + try: + base.render.clearAttrib(LightAttrib.getClassType()) + except Exception: + pass + # Avoid aggressively clearing shader state on base.render. + # During zone transitions, some nodes can briefly have singular or + # otherwise invalid transforms; forcing a full shader rebuild at the + # root can trip Panda3D assertions inside renderFrame. + except Exception: + pass + # Step ladder: + # 1: shader-auto only (no lights) + # 2: ambient only + # 3: sun (no shadows) + # 4: sun (with shadows, if enabled) + rig_spawned = False + if _bisectAllows(2): + enable_shadows = bool(_bisectAllows(4)) + enable_key = bool(_bisectAllows(3)) + # Further bisect within directional lighting to isolate camera-dependent RGB: + # key-only vs key+fill vs key+fill+rim. + try: + mode = _DIR_LIGHT_MODE + except Exception: + mode = 'all' + enable_fill = enable_key and (mode in ('key_fill', 'all')) + enable_rim = enable_key and (mode == 'all') + _spawnLightRig( + cur_spec, geom, + enable_ambient=True, + enable_key=enable_key, + enable_fill=enable_fill, + enable_rim=enable_rim, + enable_shadows=enable_shadows, + ) + rig_spawned = True + _dbg( + "spawned rig " + f"keyCastsShadows={_keyCastsShadows} shadowRes={_shadowMapRes} " + f"filmArea={_shadowFilmArea} followDist={_shadowFollowDist}" + ) + else: + _dbg("bisect: skipping light rig spawn (step<2)") + + # Only run shadow diagnostics when we actually spawned a rig. + if rig_spawned: + try: + _spawnShadowTestScene() + except Exception: + pass + # Renderer capability debug (shadow maps rely on these). + try: + gsg = base.win.getGsg() if getattr(base, 'win', None) else None + if gsg: + try: + supportsFBO = gsg.getSupportsFramebuffer() if hasattr(gsg, 'getSupportsFramebuffer') else 'n/a' + except Exception: + supportsFBO = 'n/a' + try: + supportsBasic = gsg.getSupportsBasicShaders() if hasattr(gsg, 'getSupportsBasicShaders') else 'n/a' + except Exception: + supportsBasic = 'n/a' + try: + maxTex = gsg.getMaxTextureDimension() if hasattr(gsg, 'getMaxTextureDimension') else 'n/a' + except Exception: + maxTex = 'n/a' + + _dbg(f"gsg supportsFBO={supportsFBO} supportsBasicShaders={supportsBasic} maxTexDim={maxTex}") + try: + _dbg(f"panda version={PandaSystem.getGlobalPtr().getVersionString()}") + except Exception: + pass + try: + import panda3d.core as _p3c + _dbg(f"python exe={sys.executable}") + _dbg(f"panda3d.core file={getattr(_p3c, '__file__', 'n/a')}") + except Exception: + pass + + try: + vendor = gsg.getDriverVendor() if hasattr(gsg, 'getDriverVendor') else 'n/a' + except Exception: + vendor = 'n/a' + try: + renderer = gsg.getDriverRenderer() if hasattr(gsg, 'getDriverRenderer') else 'n/a' + except Exception: + renderer = 'n/a' + try: + version = gsg.getDriverVersion() if hasattr(gsg, 'getDriverVersion') else 'n/a' + except Exception: + version = 'n/a' + _dbg(f"gsg driver vendor={vendor} renderer={renderer} version={version}") + + try: + pipe = getattr(base, 'pipe', None) + pipeType = pipe.getType().getName() if pipe and hasattr(pipe, 'getType') else 'n/a' + except Exception: + pipeType = 'n/a' + try: + iface = pipe.getInterfaceName() if pipe and hasattr(pipe, 'getInterfaceName') else 'n/a' + except Exception: + iface = 'n/a' + try: + loadDisplay = ConfigVariableString('load-display', '').value + except Exception: + loadDisplay = 'n/a' + _dbg(f"pipe type={pipeType} interface={iface} prc load-display={loadDisplay}") + except Exception: + pass + if not _supportsBasicShaders(): + _dbg("basic shaders unsupported; shadow maps disabled (using OTP drop shadows instead)") + # Deferred shadow buffer allocation check and fallback retry: + # Buffer/texture allocation happens asynchronously on the Draw thread, + # so we schedule a check after 0.5s. If allocation failed, we fall back + # to a lower resolution or disable shadows, avoiding multiple conflicting + # setShadowCaster calls in the same frame. + try: + taskMgr.remove('outdoorLightingShadowAllocDebug') + except Exception: + pass + + def _shadowAllocDebug(task): + global _shadowMapRes, _keyCastsShadows + try: + if not (_keyLightNp and not _keyLightNp.isEmpty()): + _dbg("shadowAllocDebug: key light NP missing") + return task.done + k = _keyLightNp.getNode(0) + _dbg(f"shadowAllocDebug: isShadowCaster={getattr(k, 'isShadowCaster', lambda: 'n/a')()}") + gsg = None + try: + gsg = base.win.getGsg() if getattr(base, 'win', None) else None + except Exception: + gsg = None + try: + if gsg: + _dbg( + "shadowAllocDebug: gsg " + f"supportsFBO={getattr(gsg, 'getSupportsFramebuffer', lambda: 'n/a')()} " + f"supportsBasicShaders={getattr(gsg, 'getSupportsBasicShaders', lambda: 'n/a')()} " + f"supportsDepthTex={getattr(gsg, 'getSupportsDepthTexture', lambda: 'n/a')()} " + f"maxTexDim={getattr(gsg, 'getMaxTextureDimension', lambda: 'n/a')()}" + ) + except Exception: + pass + + def _call_shadow_accessor(name: str): + if not hasattr(k, name): + return None + fn = getattr(k, name) + # Panda3D builds differ: some take (gsg, i), others (gsg) returning list-like, + # others take (i) (rare). Try common signatures. + for args in ((gsg, 0), (gsg,), (0,)): + try: + if gsg is None and args and args[0] is gsg: + continue + v = fn(*args) + _dbg(f"shadowAllocDebug: {name}{args}={'ok' if v else 'none'}") + return v + except TypeError as e: + last = e + except Exception as e: + _dbg(f"shadowAllocDebug: {name}{args} exception: {e!r}") + return None + try: + _dbg(f"shadowAllocDebug: {name} signature mismatch: {last!r}") + except Exception: + pass + return None + + buf = _call_shadow_accessor('getShadowBuffer') + + # If the light is configured to cast shadows, but the shadow buffer isn't allocating, retry at smaller resolutions. + if getattr(k, 'isShadowCaster', lambda: False)() and (buf is None) and hasattr(k, 'setShadowCaster') and gsg: + if not gsg.getSupportsBasicShaders(): + print("[DEBUG VideoSettings] shadowAllocDebug: GSG reports getSupportsBasicShaders() is False. Disabling shadow maps to prevent GL errors.") + k.setShadowCaster(False) + _keyCastsShadows = False + else: + if _shadowMapRes > 1024: + retry_res = 1024 + elif _shadowMapRes > 512: + retry_res = 512 + else: + retry_res = 0 + + if retry_res > 0: + print(f"[DEBUG VideoSettings] shadowAllocDebug: shadow buffer not allocated at {_shadowMapRes}. Falling back to {retry_res}...") + try: + k.setShadowCaster(True, retry_res, retry_res) + try: + k.setCameraMask(_SHADOW_CAM_MASK) + except Exception: + pass + _shadowMapRes = retry_res + _dbg(f"shadowAllocDebug: retry setShadowCaster({retry_res}) scheduled") + taskMgr.doMethodLater(0.5, _shadowAllocDebug, 'outdoorLightingShadowAllocDebug') + except Exception as e: + print(f"[DEBUG VideoSettings] shadowAllocDebug: retry setShadowCaster({retry_res}) failed: {e!r}") + _dbg(f"shadowAllocDebug: retry setShadowCaster({retry_res}) exception: {e!r}") + else: + print(f"[DEBUG VideoSettings] shadowAllocDebug: shadow buffer not allocated at {_shadowMapRes}. Disabling shadow maps completely.") + k.setShadowCaster(False) + _keyCastsShadows = False + return task.done + except Exception as e: + _dbg(f"shadowAllocDebug exception: {e!r}") + return task.done + + taskMgr.doMethodLater(0.5, _shadowAllocDebug, 'outdoorLightingShadowAllocDebug') + try: + la = base.render.getAttrib(LightAttrib.getClassType()) + if la is not None: + try: + num_on = la.getNumOnLights() + except Exception: + num_on = None + _dbg(f"render LightAttrib onLights={num_on}") + except Exception: + pass + try: + if _keyLightNp is not None and not _keyLightNp.isEmpty(): + _dbg(f"key light node={_keyLightNp.getNode(0)}") + except Exception: + pass + if _bisectAllows(5): + _applyFog(cur_spec) + _tintSky(cur_spec) + _applyBackgroundColor(cur_spec) + if _bisectAllows(6): + _setupProceduralSky(cur_spec) + if _OUTDOOR_SHADER_BISECT_LEVEL >= 2 and _bisectAllows(7): + _setupPostProcess(cur_spec) + if _OUTDOOR_SHADER_BISECT_LEVEL >= 3 and _bisectAllows(9): + _setupAllWater(geom, cur_spec) + _dimSkyForLights() + # Full-window + per-DisplayRegion pixel_zoom (see _maintainOutdoorLightingViewport). + _maintainOutdoorLightingViewport() + # Run before most gameplay (low sort = earlier): keep viewports patched pre-cull. + # When bisecting at shader-auto only (step 1), avoid running the update task + # since it can trigger deferred rig rebuilds or other side effects. + if _bisectAllows(2): + taskMgr.add(_lightingUpdateTask, _godRaysTaskName, sort=-60) + + _refCount += 1 + + if geom is not None and not geom.isEmpty(): + # Also clear any local light overrides on the zone root so our rig lights + # can actually affect it (and therefore generate visible shading/shadows). + try: + if hasattr(geom, 'clearLightOff'): + geom.clearLightOff() + try: + geom.clearAttrib(LightAttrib.getClassType()) + except Exception: + pass + except Exception: + pass + geom.setShaderAuto() + # Force a full normals rescan at begin() time. The pre-flatten call sets + # _NORMALS_TAG on the geom root, but distributed objects added between load + # and begin() may have attached new geometry without normals. Clear the tag + # so _ensureNormals sees all current GeomNodes, not just the original set. + try: + if geom is not None and not geom.isEmpty(): + geom.setPythonTag(_NORMALS_TAG, None) + except Exception: + pass + try: + _ensureNormals(geom) + except Exception: + pass + # Ensure nothing in the zone subtree disables shaders; shadow receiving + # requires the auto-shader path to be active. + try: + _forceShadersOnSubtree(geom) + except Exception: + pass + # Normals injection changes the vertex format; force shader regen so the + # auto-shader (including shadow sampling) gets rebuilt on the new format. + try: + _forceShaderRegen(geom) + except Exception: + pass + _forceLightingOnSubtree(geom) + _hidePlaneLikeCastersFromShadow(geom) + try: + hood = _getHood() + if hood and getattr(hood, 'sky', None) and not hood.sky.isEmpty(): + hood.sky.hide(_SHADOW_CAM_MASK) + except Exception: + pass + # Do not force a specular material on the entire zone geometry. Zone + # meshes are frequently large, use vertex colors, and may contain + # mixed material setups; forcing a specular material globally can + # introduce view-dependent color artifacts. Specular remains available + # opt-in via `lighting-specular-enabled` on specific subtrees. + try: + la = geom.getAttrib(LightAttrib.getClassType()) + _dbg(f"geom LightAttrib present={la is not None}") + except Exception: + pass + try: + ra = base.render.getAttrib(LightAttrib.getClassType()) + _dbg(f"render LightAttrib present={ra is not None}") + except Exception: + pass + + # Debug: shader state + normals presence (flat lighting can be caused by missing normals). + try: + if _debugEnabled(): + try: + _dbg(f"geom hasShader={'yes' if geom.hasShader() else 'no'}") + except Exception: + pass + try: + _dbg(f"render hasShader={'yes' if base.render.hasShader() else 'no'}") + except Exception: + pass + try: + sa = base.render.getAttrib(ShaderAttrib.getClassType()) + _dbg(f"render ShaderAttrib present={sa is not None}") + if sa is not None: + try: + sh = sa.getShader() + _dbg(f"render ShaderAttrib shader={'ok' if sh else 'none'}") + except Exception: + pass + for meth in ('getAutoShader', 'getFlag'): + if hasattr(sa, meth): + try: + _dbg(f"render ShaderAttrib {meth}()={getattr(sa, meth)()}") + except Exception: + pass + except Exception: + pass + try: + sa = geom.getAttrib(ShaderAttrib.getClassType()) + _dbg(f"geom ShaderAttrib present={sa is not None}") + if sa is not None: + try: + sh = sa.getShader() + _dbg(f"geom ShaderAttrib shader={'ok' if sh else 'none'}") + except Exception: + pass + for meth in ('getAutoShader', 'getFlag'): + if hasattr(sa, meth): + try: + _dbg(f"geom ShaderAttrib {meth}()={getattr(sa, meth)()}") + except Exception: + pass + except Exception: + pass + try: + nodes = geom.findAllMatches('**/+GeomNode') + total = nodes.getNumPaths() + with_normals = 0 + checked = min(total, 50) + for i in range(checked): + gnp = nodes.getPath(i).node() + if gnp.getNumGeoms() <= 0: + continue + g = gnp.getGeom(0) + vdata = g.getVertexData() + if vdata and vdata.hasColumn('normal'): + with_normals += 1 + _dbg(f"geom geomNodes={total} checked={checked} withNormals={with_normals}") + except Exception: + pass + except Exception: + pass + # Scan for lamp/lantern nodes to use as night-light emitters. + # Only scan on first begin() (refCount was 0 before increment). + if _refCount == 1: + global _lampGeomNps + _lampGeomNps = _scanForLampNodes(geom) + + lav = getattr(base, 'localAvatar', None) + if lav is not None and not lav.isEmpty(): + lav.setShaderAuto() + + +def end(geom=None) -> None: + """Deactivate the outdoor lighting rig.""" + _syncBase() + global _refCount, _activeGeom + if not _wantFx(): + return + _dbg(f"end(refCount={_refCount}) geom={'ok' if (geom is not None and not geom.isEmpty()) else 'none'}") + + if geom is not None and not geom.isEmpty(): + geom.clearShader() + _clearDefaultSpecularMaterial(geom) + lav = getattr(base, 'localAvatar', None) + if lav is not None and not lav.isEmpty(): + lav.clearShader() + + if _refCount > 0: + _refCount -= 1 + + if _refCount == 0: + _activeGeom = None + taskMgr.remove(_godRaysTaskName) + _destroyPostProcess() + _cleanupAllWater() + _clearFog() + _clearSkyTint() + _restoreBackgroundColor() + _destroyLightRig() + _restoreSkyLighting() + _destroyProceduralSky() + global _lampGeomNps + _lampGeomNps = [] + + +def shadeExtraSubtree(np) -> None: + """Enable auto-shading on an extra outdoor NodePath.""" + _syncBase() + if not _wantFx() or np is None or np.isEmpty(): + return + np.setShaderAuto() + _forceLightingOnSubtree(np) + _hidePlaneLikeCastersFromShadow(np) + _applyDefaultSpecularMaterial(np) + + +def clearExtraSubtree(np) -> None: + """Remove auto-shading from an extra outdoor NodePath.""" + _syncBase() + if not _wantFx() or np is None or np.isEmpty(): + return + np.clearShader() + _clearDefaultSpecularMaterial(np) + +def _maybeRebuildLightRig(spec: dict) -> None: + global _lightRig, _keyCastsShadows, _shadowMapRes, _prevShadowCasterState, _activeGeom + # Check if shadow settings changed. + current_wants_shadow = _keyCastsShadows + current_res = _shadowMapRes + + wants_shadow = (spec.get('shadowCaster', False) + and _wantDynamicShadows() + and _shadowQuality() != 'off') + + # Determine the resolution snap-down + res = 1024 + if wants_shadow: + try: + res = _scaledShadowRes(spec) + # Apply clamps similar to _spawnLightRig + gsg = base.win.getGsg() if getattr(base, 'win', None) else None + if gsg: + try: + max_dim = int(gsg.getMaxTextureDimension()) + if max_dim and res > max_dim: + res = max_dim + except Exception: + pass + if res >= 4096: res = 4096 + elif res >= 2048: res = 2048 + elif res >= 1024: res = 1024 + else: res = 512 + except Exception: + pass + + need_rebuild = (_lightRig is None or _lightRig.isEmpty() or + wants_shadow != current_wants_shadow or + (wants_shadow and res != current_res)) + + if need_rebuild: + print(f"[DEBUG VideoSettings] _maybeRebuildLightRig: Rebuilding light rig (wants_shadow={wants_shadow}, res={res})...") + savedBounds = None + if _shadowFocusPos is not None: + savedBounds = (Vec3(_shadowFocusPos), float(_shadowSceneRadius)) + _destroyLightRig() + _spawnLightRig(spec, geom=_activeGeom, shadowBounds=savedBounds) + if _activeGeom is not None and not _activeGeom.isEmpty(): + try: + _activeGeom.setShaderAuto() + _forceShadersOnSubtree(_activeGeom) + _forceShaderRegen(_activeGeom) + _forceLightingOnSubtree(_activeGeom) + _hidePlaneLikeCastersFromShadow(_activeGeom) + except Exception as e: + print(f"[DEBUG VideoSettings] _maybeRebuildLightRig: geom shader regen exception: {e!r}") + lav = getattr(base, 'localAvatar', None) + if lav is not None and not lav.isEmpty(): + try: + _forceShaderRegen(lav) + except Exception as e: + print(f"[DEBUG VideoSettings] _maybeRebuildLightRig: localAvatar shader regen exception: {e!r}") + print("[DEBUG VideoSettings] _maybeRebuildLightRig: Light rig rebuilt successfully.") + else: + print("[DEBUG VideoSettings] _maybeRebuildLightRig: Shadow settings unchanged. Updating light rig in-place...") + _prevShadowCasterState = wants_shadow + _applyProfileLive(spec) + print("[DEBUG VideoSettings] _maybeRebuildLightRig: Light rig update complete.") + + +def refreshSettings() -> None: + """Hot-reload all active lighting parameters from current user settings. + + Called by the Lighting settings tab whenever the player changes a slider + or toggle. All systems are updated in-place where possible. + """ + print("[DEBUG VideoSettings] OutdoorLighting.refreshSettings: starting...") + _syncBase() + if _refCount == 0: + print("[DEBUG VideoSettings] OutdoorLighting.refreshSettings: refCount is 0, returning.") + return + + # In Cull/Draw threading mode the Draw thread is always active (2 frames + # behind App) and holds the GL context mutex while rendering. Destroying + # and recreating shadow-map FBOs (via _destroyLightRig / _spawnLightRig) + # and bloom FBOs (via _destroyPostProcess / _setupPostProcess) while Draw + # holds the GL context causes a permanent deadlock. syncFrame() blocks + # until the current Cull and Draw passes both finish, giving us a clean + # pipeline before we touch any offscreen buffers. + try: + if base is not None and base.config.GetString('threading-model', ''): + print("[DEBUG VideoSettings] OutdoorLighting.refreshSettings: calling graphicsEngine.syncFrame()...") + base.graphicsEngine.syncFrame() + print("[DEBUG VideoSettings] OutdoorLighting.refreshSettings: graphicsEngine.syncFrame() returned.") + except Exception as e: + print(f"[DEBUG VideoSettings] OutdoorLighting.refreshSettings: syncFrame exception: {e!r}") + + # Always keep OTP blob/drop shadows in sync with user settings. + # (These work even when shaders are unavailable.) + if _OTPDropshadow: + try: + shadow_flag = 1 if _settingsBool('dynamic-shadows', True) else 0 + print(f"[DEBUG VideoSettings] OutdoorLighting.refreshSettings: setting global drop shadow flag={shadow_flag}...") + _OTPDropshadow.setGlobalDropShadowFlag(shadow_flag) + except Exception as e: + print(f"[DEBUG VideoSettings] OutdoorLighting.refreshSettings: setGlobalDropShadowFlag exception: {e!r}") + try: + shadow_strength = float(_settingsFloat('drop-shadow-strength', 0.5)) + print(f"[DEBUG VideoSettings] OutdoorLighting.refreshSettings: setting global drop shadow gray level={shadow_strength}...") + _OTPDropshadow.setGlobalDropShadowGrayLevel(shadow_strength) + except Exception as e: + print(f"[DEBUG VideoSettings] OutdoorLighting.refreshSettings: setGlobalDropShadowGrayLevel exception: {e!r}") + + spec = _getActiveSpec() + + # Maybe rebuild light rig (avoids recreation if settings are unchanged). + _maybeRebuildLightRig(spec) + + print("[DEBUG VideoSettings] OutdoorLighting.refreshSettings: applying fog and sky color...") + _clearFog() + _applyFog(spec) + _tintSky(spec) + _applyBackgroundColor(spec) + + # Procedural sky toggle. + want_sky = _wantProceduralSky() + print(f"[DEBUG VideoSettings] OutdoorLighting.refreshSettings: procedural sky want_sky={want_sky}") + if want_sky: + if _proceduralSky is None: + print("[DEBUG VideoSettings] OutdoorLighting.refreshSettings: setting up procedural sky...") + _setupProceduralSky(spec) + elif _proceduralSky is not None: + print("[DEBUG VideoSettings] OutdoorLighting.refreshSettings: updating procedural sky...") + _proceduralSky.setStyle(_activeStyle) + _proceduralSky.update(spec, _timeOfDay) + else: + print("[DEBUG VideoSettings] OutdoorLighting.refreshSettings: destroying procedural sky...") + _destroyProceduralSky() + _tintSky(spec) + + # Bloom + sun-ray overlay + print("[DEBUG VideoSettings] OutdoorLighting.refreshSettings: destroying post process...") + _destroyPostProcess() + if _OUTDOOR_SHADER_BISECT_LEVEL >= 2: + print("[DEBUG VideoSettings] OutdoorLighting.refreshSettings: setting up post process...") + _setupPostProcess(spec) + print("[DEBUG VideoSettings] OutdoorLighting.refreshSettings: finished successfully.") + + +def setTimeOfDay(hours: float) -> None: + """Set the current in-game time of day (0–24).""" + _syncBase() + global _timeOfDay + _timeOfDay = float(hours) % 24.0 + if _refCount > 0: + cur = _getActiveSpec() + _maybeRebuildLightRig(cur) + _clearFog() + _applyFog(cur) + _tintSky(cur) + _applyBackgroundColor(cur) + _updateBloomLive(cur) + if _proceduralSky: + _proceduralSky.update(cur, _timeOfDay) + + +def setupWaterReflection(waterNodePath, profile: dict | None = None) -> None: + """Manually set up planar reflections on a specific water NodePath.""" + _syncBase() + if not _wantFx() or waterNodePath is None or waterNodePath.isEmpty(): + return + base_spec = _ZONE_PROFILES.get(_activeStyle, _ZONE_PROFILES['playground']) + merged = dict(base_spec) + if profile: + merged.update(profile) + setup = _setupWaterNode(waterNodePath, merged) + if setup: + _waterSetups.append(setup) + + +def setWaterReflectionQuality(level: str) -> None: + """Adjust reflection quality for all active water surfaces.""" + _syncBase() + if level == 'off': + _cleanupAllWater() + return + spec = _getActiveSpec() + spec_copy = dict(spec) + spec_copy['waterReflQuality'] = level + for ws in list(_waterSetups): + np = ws.get('np') + try: + taskMgr.remove(ws['taskName']) + except Exception: + pass + try: + cam = ws.get('camera') + if cam and not cam.isEmpty(): + cam.removeNode() + except Exception: + pass + try: + buf = ws.get('buffer') + if buf: + base.graphicsEngine.removeWindow(buf) + except Exception: + pass + if np and not np.isEmpty(): + new_setup = _setupWaterNode(np, spec_copy) + if new_setup: + _waterSetups.append(new_setup) + _waterSetups[:] = [ws for ws in _waterSetups + if ws.get('taskName', '').startswith('waterReflTask_')] diff --git a/toontown/hood/ProceduralSky.py b/toontown/hood/ProceduralSky.py new file mode 100644 index 0000000..f45dd2e --- /dev/null +++ b/toontown/hood/ProceduralSky.py @@ -0,0 +1,410 @@ +"""Procedural code-generated sky system for Toontown. + +Replaces the hood.sky model-based sky with a GLSL-driven atmospheric sky dome +that includes: + • Rayleigh + Mie scattering sky gradient + • Wide sunset/sunrise horizon corona + • Visible SUN DISC with corona, limb darkening, blinding glare + • Volumetric-looking FBM cumulus clouds with domain warping + • Twinkling star field with spectral colour variation + • Moon disc with surface detail and corona (all night zones) + • Full day/night cycle integration + +Usage (from SkyUtil / OutdoorLighting) +────────────────────────────────────── + from toontown.hood.ProceduralSky import ProceduralSky + + sky = ProceduralSky() + # Prefer the lens NodePath (e.g. base.cam) so the dome matches the view matrix + # used for rendering; base.camera is fine when it coincides with the lens. + sky.attach(base.cam) + sky.update(spec, timeOfDay) # call each frame (or at least on spec changes) + sky.detach() # cleanup on zone exit + +The ProceduralSky.update() signature accepts the same 'spec' dict as +OutdoorLighting zone profiles, so integration is zero-cost. + +Per-zone sky parameters (added to _ZONE_PROFILES by OutdoorLighting): + cloudCoverage – 0.0–1.0 + cloudSpeed – relative speed multiplier + cloudSharpness – 0.0 (soft) – 1.0 (sharp) + turbidity – 1.0–8.0 (Mie haze) + starBrightness – 0.0–1.0 + moonEnabled – bool (all night zones, not just DL) + moonDir – (h,p,r) HPR for moon direction (matches keyHpr format) + skyExposure – scalar (passed to post-process; default 1.0) + sunBlindStrength– 0.0–1.0 blinding glare when looking at sun (default 0.85) +""" + +from __future__ import annotations + +import math +import os + +from panda3d.core import ( + Geom, + GeomNode, + GeomTriangles, + GeomVertexData, + GeomVertexFormat, + GeomVertexWriter, + Filename, + NodePath, + Shader, + Vec3, + Vec4, +) + +from direct.showbase.ShowBaseGlobal import globalClock +from direct.task.TaskManagerGlobal import taskMgr + +import builtins +base = getattr(builtins, 'base', None) + +_SHADER_DIR_CANDIDATES = ( + os.path.join(os.path.dirname(__file__), '..', 'shaders'), + os.path.join(os.path.dirname(__file__), 'shaders'), + os.path.join(os.path.dirname(__file__), '..', '..', 'shaders'), +) +_SHADER_DIR = next((d for d in _SHADER_DIR_CANDIDATES if os.path.exists(d)), _SHADER_DIR_CANDIDATES[0]) +_SKY_VERT = os.path.join(_SHADER_DIR, 'sky.vert.glsl') +_SKY_FRAG = os.path.join(_SHADER_DIR, 'sky.frag.glsl') + +_SKY_RADIUS = 950.0 # units – large enough to contain all Toontown geometry +_CLOUD_BASE_SPEED = 0.60 # base cloud animation speed (modified per zone) + +_sky_shader: Shader | None = None + + +def _loadSkyShader() -> Shader | None: + global _sky_shader + if _sky_shader is not None: + return _sky_shader + print(f"[DEBUG VideoSettings] _loadSkyShader: starting...") + try: + try: + from toontown.hood import OutdoorLighting as osl + print(f"[DEBUG VideoSettings] _loadSkyShader: imported from toontown.hood") + except ImportError: + import OutdoorLighting as osl + print(f"[DEBUG VideoSettings] _loadSkyShader: imported from root") + bisect_level = getattr(osl, '_OUTDOOR_SHADER_BISECT_LEVEL', 0) + print(f"[DEBUG VideoSettings] _loadSkyShader: osl._OUTDOOR_SHADER_BISECT_LEVEL={bisect_level}") + if bisect_level < 1: + print(f"[DEBUG VideoSettings] _loadSkyShader: bisect level < 1, returning None") + return None + except Exception as e: + print(f"[DEBUG VideoSettings] _loadSkyShader: exception in osl import/check: {e!r}") + pass + try: + vp_os = os.path.normpath(_SKY_VERT) + fp_os = os.path.normpath(_SKY_FRAG) + print(f"[DEBUG VideoSettings] _loadSkyShader: vp_os={vp_os}, fp_os={fp_os}") + vp_exists = os.path.isfile(vp_os) + fp_exists = os.path.isfile(fp_os) + print(f"[DEBUG VideoSettings] _loadSkyShader: vp_exists={vp_exists}, fp_exists={fp_exists}") + if not (vp_exists and fp_exists): + print(f"[DEBUG VideoSettings] _loadSkyShader: file missing, returning None") + return None + # On Windows, Panda3D's shader loader expects Panda-style paths (eg + # `/c/Users/...`) rather than raw OS paths with backslashes. + vp = Filename.fromOsSpecific(vp_os) + fp = Filename.fromOsSpecific(fp_os) + try: + vp.makeTrueCase() + fp.makeTrueCase() + except Exception: + pass + print(f"[DEBUG VideoSettings] _loadSkyShader: loading shader...") + _sky_shader = Shader.load(Shader.SL_GLSL, vp, fp) + print(f"[DEBUG VideoSettings] _loadSkyShader: load returned {_sky_shader}") + return _sky_shader + except Exception as e: + print(f"[DEBUG VideoSettings] _loadSkyShader: exception: {e!r}") + import traceback; traceback.print_exc() + _sky_shader = None + return None + + +def _makeSkySphereMesh(radius: float = _SKY_RADIUS, + latSegs: int = 18, + lonSegs: int = 36) -> NodePath: + """Build an inward-facing UV sphere NodePath. + + The sphere is centred at the origin in model space. When attached to the + camera NodePath the camera is always at the sphere centre, so every vertex + position is a ray direction from the camera. + """ + vfmt = GeomVertexFormat.getV3() + vdata = GeomVertexData('skyDomeMesh', vfmt, Geom.UHStatic) + vdata.setNumRows((latSegs + 1) * (lonSegs + 1)) + vwrite = GeomVertexWriter(vdata, 'vertex') + + for lat in range(latSegs + 1): + phi = math.pi * lat / latSegs # 0 → π (north pole → south pole) + sp = math.sin(phi) + cp = math.cos(phi) + for lon in range(lonSegs + 1): + theta = 2.0 * math.pi * lon / lonSegs + x = radius * sp * math.cos(theta) + y = radius * sp * math.sin(theta) + z = radius * cp + vwrite.addData3(x, y, z) + + tris = GeomTriangles(Geom.UHStatic) + stride = lonSegs + 1 + for lat in range(latSegs): + for lon in range(lonSegs): + v0 = lat * stride + lon + v1 = lat * stride + lon + 1 + v2 = (lat + 1) * stride + lon + v3 = (lat + 1) * stride + lon + 1 + # Inward-facing: flip winding compared to outward sphere. + tris.addVertices(v0, v2, v1) + tris.addVertices(v1, v2, v3) + tris.closePrimitive() + + geom = Geom(vdata) + geom.addPrimitive(tris) + gnode = GeomNode('skyDomeGeom') + gnode.addGeom(geom) + return NodePath(gnode) + + +def _dir_world_to_cam(world_dir: Vec3) -> Vec3: + """Map a world-space *direction* into the active camera's local space. + + The sky dome is parented to ``base.cam``, so ``vDir`` in the GLSL fragment + shader is in **camera space**. ``sunDir`` / ``moonDir`` must match that + space or dot products (sun disc, clouds, Mie) are wrong and the sky looks + like a flat clear colour with no sun or clouds. + """ + try: + cam = getattr(base, 'cam', None) + rnp = getattr(base, 'render', None) + if cam is None or rnp is None or cam.isEmpty() or rnp.isEmpty(): + return Vec3(world_dir) + v = cam.getRelativeVector(rnp, Vec3(world_dir)) + ln = v.length() + if ln > 1.0e-7: + v /= ln + return v + except Exception: + return Vec3(world_dir) + + +def _hprToDir(h_deg: float, p_deg: float) -> Vec3: + """Convert a Panda3D HPR heading/pitch to a world-space direction vector. + + The direction returned is the *forward* vector that a node with (H, P, 0) + orientation points toward. For the sun key light this is the direction the + light shines (scene ← sun); for sun position in the sky pass the negated + result. + """ + h = math.radians(h_deg) + p = math.radians(p_deg) + # Panda3D right-hand Y-forward Z-up: + # H rotates around Z (clockwise from above, i.e. left-hand around Z) + # P rotates around X after H + x = math.sin(h) * math.cos(p) + y = -math.cos(h) * math.cos(p) + z = -math.sin(p) + return Vec3(x, y, z) + + +# ───────────────────────────────────────────────────────────────────────────── +# Per-zone cloud + sky parameters defaults +# (OutdoorLighting zones may override any of these in their profile dict) +# ───────────────────────────────────────────────────────────────────────────── + +_ZONE_SKY_DEFAULTS: dict[str, dict] = { + 'tt': {'cloudCoverage': 0.42, 'cloudSpeed': 0.70, 'cloudSharpness': 0.55, + 'turbidity': 2.5, 'starBrightness': 0.0, 'moonEnabled': False}, + 'dd': {'cloudCoverage': 0.88, 'cloudSpeed': 1.10, 'cloudSharpness': 0.20, + 'turbidity': 5.5, 'starBrightness': 0.0, 'moonEnabled': False}, + 'dg': {'cloudCoverage': 0.28, 'cloudSpeed': 0.55, 'cloudSharpness': 0.70, + 'turbidity': 1.8, 'starBrightness': 0.0, 'moonEnabled': False}, + 'mm': {'cloudCoverage': 0.55, 'cloudSpeed': 0.85, 'cloudSharpness': 0.45, + 'turbidity': 3.5, 'starBrightness': 0.0, 'moonEnabled': False}, + 'br': {'cloudCoverage': 0.78, 'cloudSpeed': 1.40, 'cloudSharpness': 0.15, + 'turbidity': 6.0, 'starBrightness': 0.0, 'moonEnabled': False}, + 'dl': {'cloudCoverage': 0.22, 'cloudSpeed': 0.25, 'cloudSharpness': 0.50, + 'turbidity': 1.5, 'starBrightness': 0.92, 'moonEnabled': True, + 'moonDir': (225, -55, 0)}, + 'gs': {'cloudCoverage': 0.32, 'cloudSpeed': 0.90, 'cloudSharpness': 0.50, + 'turbidity': 3.0, 'starBrightness': 0.0, 'moonEnabled': False}, + 'estate': {'cloudCoverage': 0.38, 'cloudSpeed': 0.60, 'cloudSharpness': 0.55, + 'turbidity': 2.2, 'starBrightness': 0.0, 'moonEnabled': False}, + 'playground': {'cloudCoverage': 0.35, 'cloudSpeed': 0.65, 'cloudSharpness': 0.50, + 'turbidity': 2.4, 'starBrightness': 0.0, 'moonEnabled': False}, + # HQ zones: minimal sky (player rarely sees it indoors/dense area) + 'sellbot_hq': {'cloudCoverage': 0.95, 'cloudSpeed': 0.30, 'cloudSharpness': 0.05, + 'turbidity': 8.0, 'starBrightness': 0.0, 'moonEnabled': False}, + 'cashbot_hq': {'cloudCoverage': 0.85, 'cloudSpeed': 0.40, 'cloudSharpness': 0.10, + 'turbidity': 7.0, 'starBrightness': 0.0, 'moonEnabled': False}, + 'lawbot_hq': {'cloudCoverage': 0.92, 'cloudSpeed': 0.20, 'cloudSharpness': 0.10, + 'turbidity': 7.5, 'starBrightness': 0.0, 'moonEnabled': False}, + 'bossbot_hq': {'cloudCoverage': 0.99, 'cloudSpeed': 0.10, 'cloudSharpness': 0.05, + 'turbidity': 8.0, 'starBrightness': 0.0, 'moonEnabled': False}, + 'cog': {'cloudCoverage': 0.75, 'cloudSpeed': 0.50, 'cloudSharpness': 0.20, + 'turbidity': 6.0, 'starBrightness': 0.0, 'moonEnabled': False}, + # Street variants + 'tt_street': {'cloudCoverage': 0.40, 'cloudSpeed': 0.68, 'cloudSharpness': 0.55, + 'turbidity': 2.4, 'starBrightness': 0.0, 'moonEnabled': False}, + 'dd_street': {'cloudCoverage': 0.90, 'cloudSpeed': 1.20, 'cloudSharpness': 0.18, + 'turbidity': 5.8, 'starBrightness': 0.0, 'moonEnabled': False}, + 'dg_street': {'cloudCoverage': 0.24, 'cloudSpeed': 0.52, 'cloudSharpness': 0.72, + 'turbidity': 1.8, 'starBrightness': 0.0, 'moonEnabled': False}, + 'mm_street': {'cloudCoverage': 0.52, 'cloudSpeed': 0.88, 'cloudSharpness': 0.42, + 'turbidity': 3.6, 'starBrightness': 0.0, 'moonEnabled': False}, + 'br_street': {'cloudCoverage': 0.80, 'cloudSpeed': 1.50, 'cloudSharpness': 0.12, + 'turbidity': 6.2, 'starBrightness': 0.0, 'moonEnabled': False}, + 'dl_street': {'cloudCoverage': 0.20, 'cloudSpeed': 0.22, 'cloudSharpness': 0.48, + 'turbidity': 1.5, 'starBrightness': 0.88, 'moonEnabled': True, + 'moonDir': (225, -55, 0)}, + 'golf_course': {'cloudCoverage': 0.30, 'cloudSpeed': 0.65, 'cloudSharpness': 0.60, + 'turbidity': 2.2, 'starBrightness': 0.0, 'moonEnabled': False}, +} + + +# ───────────────────────────────────────────────────────────────────────────── +# ProceduralSky class +# ───────────────────────────────────────────────────────────────────────────── + +class ProceduralSky: + """Manages a GLSL-driven procedural sky sphere. + + One instance per hood / zone. OutdoorLighting creates and destroys it + alongside the light rig. + """ + + _TASK_NAME = 'proceduralSkyTask' + + def __init__(self) -> None: + self._skyNp: NodePath | None = None + self._time: float = 0.0 + self._activeStyle: str = 'playground' + self._attached: bool = False + + # ── Public API ──────────────────────────────────────────────────────────── + + def attach(self, parent: NodePath, style: str = 'playground') -> None: + """Create the sky sphere and attach it to *parent* (usually camera).""" + if self._attached: + return + shader = _loadSkyShader() + if shader is None: + return # graceful fallback – sky model will be used instead + + try: + self._skyNp = _makeSkySphereMesh() + self._skyNp.reparentTo(parent) + self._skyNp.setDepthTest(False) + self._skyNp.setDepthWrite(False) + self._skyNp.setLightOff(1) + # Ensure the procedural sky is drawn after any legacy/model sky that + # might also live in the background bin. + self._skyNp.setBin('background', 1000) + self._skyNp.setTwoSided(True) + self._skyNp.setShader(shader) + self._skyNp.setShaderAuto(False) + + self._activeStyle = style + self._attached = True + self._time = 0.0 + + # No update task here – OutdoorLighting's main task calls update(). + except Exception as e: + import traceback; traceback.print_exc() + self._skyNp = None + + def update(self, spec: dict, timeOfDay: float = 12.0) -> None: + """Push zone spec parameters as shader uniforms.""" + if not self._attached or self._skyNp is None or self._skyNp.isEmpty(): + return + + self._time += globalClock.getDt() + + # Resolve sky sub-parameters (check spec first, fall back to defaults). + style = self._activeStyle + skyDef = _ZONE_SKY_DEFAULTS.get(style, _ZONE_SKY_DEFAULTS['playground']) + cov = float(spec.get('cloudCoverage', skyDef.get('cloudCoverage', 0.4))) + spd = float(spec.get('cloudSpeed', skyDef.get('cloudSpeed', 0.6))) + sharp = float(spec.get('cloudSharpness', skyDef.get('cloudSharpness', 0.5))) + turb = float(spec.get('turbidity', skyDef.get('turbidity', 2.5))) + stars = float(spec.get('starBrightness', skyDef.get('starBrightness', 0.0))) + moonOn = float(1 if spec.get('moonEnabled', skyDef.get('moonEnabled', False)) else 0) + moonHpr = spec.get('moonDir', skyDef.get('moonDir', (0, -45, 0))) + + # Sun direction: forward vector of key light (direction light shines). + keyHpr = spec.get('keyHpr', (135, -42, 0)) + lightFwd = _hprToDir(keyHpr[0], keyHpr[1]) + # Sun position in sky = opposite of light direction (world space). + sunDirWorld = Vec3(-lightFwd.x, -lightFwd.y, -lightFwd.z) + + moonDirWorld = Vec3(0, 0, 1) + if moonOn > 0.5: + mfwd = _hprToDir(moonHpr[0], moonHpr[1]) + moonDirWorld = Vec3(-mfwd.x, -mfwd.y, -mfwd.z) + + # Must match camera-space ``vDir`` in the shader (dome is under base.cam). + sunDirCam = _dir_world_to_cam(sunDirWorld) + moonDirCam = _dir_world_to_cam(moonDirWorld) + + keyColor = Vec4(*spec.get('key', (1, 1, 1, 1))) + clearColor = spec.get('clearColor', (0.4, 0.6, 0.85, 1.0)) + fogColorV = spec.get('fogColor', (0.6, 0.7, 0.85, 1.0)) + skyScaleV = spec.get('skyScale', (1, 1, 1, 1)) + + # Derive zenith and horizon colours from clearColor and fogColor. + # zenith = clearColor (the "perfect overhead blue") + # horizon = blend clearColor → fogColor + zenith = Vec3(clearColor[0], clearColor[1], clearColor[2]) + horizon = Vec3(fogColorV[0] * 0.85, fogColorV[1] * 0.85, fogColorV[2] * 0.85) + + # Day/night: fade stars from *world* sun elevation (stable when camera tilts). + isDaytime = max(0.0, min(1.0, (sunDirWorld.z + 0.2) * 4.0)) + effectiveStars = stars * (1.0 - isDaytime) + + # Sun blind strength: default 0.85 (strong cinematic glare), clamped 0–1 + blindStr = float(spec.get('sunBlindStrength', 0.85)) + blindStr = max(0.0, min(1.0, blindStr)) + + try: + np = self._skyNp + np.setShaderInput('sunDir', sunDirCam) + np.setShaderInput('sunWorldElev', float(sunDirWorld.z)) + np.setShaderInput('sunColor', keyColor) + np.setShaderInput('zenithColor', zenith) + np.setShaderInput('horizonColor', horizon) + np.setShaderInput('fogColor', Vec3(fogColorV[0], fogColorV[1], fogColorV[2])) + np.setShaderInput('cloudCoverage', cov) + np.setShaderInput('cloudSpeed', spd * _CLOUD_BASE_SPEED) + np.setShaderInput('cloudSharpness', sharp) + np.setShaderInput('turbidity', turb) + np.setShaderInput('starBrightness', effectiveStars) + np.setShaderInput('moonEnabled', moonOn) + np.setShaderInput('moonDir', moonDirCam) + np.setShaderInput('moonColor', Vec4(*spec.get('key', (0.5, 0.6, 1, 1)))) + np.setShaderInput('time', self._time) + np.setShaderInput('skyScale', Vec4(*skyScaleV)) + # New feature uniforms + np.setShaderInput('sunDiscEnabled', 1.0) + np.setShaderInput('sunBlindStrength', blindStr) + except Exception: + pass + + def setStyle(self, style: str) -> None: + """Update which zone sky defaults to use.""" + self._activeStyle = style + + def isActive(self) -> bool: + return self._attached and self._skyNp is not None and not self._skyNp.isEmpty() + + def detach(self) -> None: + """Remove the sky sphere and clean up.""" + if self._skyNp and not self._skyNp.isEmpty(): + self._skyNp.removeNode() + self._skyNp = None + self._attached = False + self._time = 0.0 diff --git a/toontown/quest/LegacyQuestDict.py b/toontown/quest/LegacyQuestDict.py new file mode 100644 index 0000000..abc4643 --- /dev/null +++ b/toontown/quest/LegacyQuestDict.py @@ -0,0 +1,15743 @@ +from toontown.quest.Quests import * + +QuestDict = { + # --- TTC SOLO VETERAN ARC (TT_TIER) --- + # 60 total quests = 15 chains x 4 steps. + # Hard caps: + # - defeat tasks: <= 10 cogs + # - collect tasks: <= 5 items, chance >= 95 + # - buildings: only 1-story (floors=1) + # + # Shopkeeper-driven structure: + # These chains bounce between TTC shopkeepers (2002/2003/2005) and ToonHQ. + + # Chain 01 (reward 600: +10 beans) + 9000: (TT_TIER, Start, (VisitQuest,), Any, 2002, NA, 9001, DefaultDialog), + 9001: (TT_TIER, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 4, Any), Same, 2002, NA, 9002, DefaultDialog), + 9002: (TT_TIER, Cont, (VisitQuest,), Same, 2003, NA, 9003, DefaultDialog), + 9003: (TT_TIER, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 6, Any), Same, ToonHQ, 600, NA, DefaultDialog), + + # Chain 02 (reward 200: pouch 25) + 9004: (TT_TIER, Start, (VisitQuest,), Any, 2003, NA, 9005, DefaultDialog), + 9005: (TT_TIER, Cont, (DeliverGagQuest, 4, ToontownBattleGlobals.SQUIRT_TRACK, 0), Same, 2003, NA, 9006, DefaultDialog), + 9006: (TT_TIER, Cont, (VisitQuest,), Same, 2005, NA, 9007, DefaultDialog), + 9007: (TT_TIER, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 8, Any), Same, ToonHQ, 200, NA, DefaultDialog), + + # Chain 03 (reward 601: +20 beans) + 9008: (TT_TIER, Start, (VisitQuest,), Any, 2005, NA, 9009, DefaultDialog), + 9009: (TT_TIER, Cont, (RecoverItemQuest, ToontownGlobals.ToontownCentral, 3, 20, 95, Any, 'type'), Same, 2005, NA, 9010, DefaultDialog), + 9010: (TT_TIER, Cont, (VisitQuest,), Same, 2002, NA, 9011, DefaultDialog), + 9011: (TT_TIER, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 8, Any), Same, ToonHQ, 601, NA, DefaultDialog), + + # Chain 04 (reward 700: max money 50) + 9012: (TT_TIER, Start, (VisitQuest,), Any, ToonHQ, NA, 9013, DefaultDialog), + 9013: (TT_TIER, Cont, (PhoneQuest,), Same, ToonHQ, NA, 9014, DefaultDialog), + 9014: (TT_TIER, Cont, (VisitQuest,), Same, 2003, NA, 9015, DefaultDialog), + 9015: (TT_TIER, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 10, Any), Same, ToonHQ, 700, NA, DefaultDialog), + + # Chain 05 (reward 100: +1 laff) + 9016: (TT_TIER, Start, (VisitQuest,), Any, 2002, NA, 9017, DefaultDialog), + 9017: (TT_TIER, Cont, (DeliverGagQuest, 4, ToontownBattleGlobals.THROW_TRACK, 0), Same, 2002, NA, 9018, DefaultDialog), + 9018: (TT_TIER, Cont, (VisitQuest,), Same, ToonHQ, NA, 9019, DefaultDialog), + 9019: (TT_TIER, Cont, (BuildingQuest, ToontownGlobals.ToontownCentral, 1, Any, 1), Same, ToonHQ, 100, NA, DefaultDialog), + + # Chain 06 (reward 602: +40 beans) + 9020: (TT_TIER, Start, (VisitQuest,), Any, 2005, NA, 9021, DefaultDialog), + 9021: (TT_TIER, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 10, Any), Same, 2005, NA, 9022, DefaultDialog), + 9022: (TT_TIER, Cont, (VisitQuest,), Same, 2003, NA, 9023, DefaultDialog), + 9023: (TT_TIER, Cont, (RecoverItemQuest, ToontownGlobals.ToontownCentral, 5, 20, 95, Any, 'type'), Same, ToonHQ, 602, NA, DefaultDialog), + + # Chain 07 (reward 201: pouch 30) + 9024: (TT_TIER, Start, (VisitQuest,), Any, 2003, NA, 9025, DefaultDialog), + 9025: (TT_TIER, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 10, Any), Same, 2003, NA, 9026, DefaultDialog), + 9026: (TT_TIER, Cont, (VisitQuest,), Same, 2002, NA, 9027, DefaultDialog), + 9027: (TT_TIER, Cont, (BuildingQuest, ToontownGlobals.ToontownCentral, 1, Any, 1), Same, ToonHQ, 201, NA, DefaultDialog), + + # Chain 08 (reward 500: quest carry 2) + 9028: (TT_TIER, Start, (VisitQuest,), Any, 2002, NA, 9029, DefaultDialog), + 9029: (TT_TIER, Cont, (RecoverItemQuest, ToontownGlobals.ToontownCentral, 4, 20, 95, Any, 'type'), Same, 2002, NA, 9030, DefaultDialog), + 9030: (TT_TIER, Cont, (VisitQuest,), Same, 2005, NA, 9031, DefaultDialog), + 9031: (TT_TIER, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 10, Any), Same, ToonHQ, 500, NA, DefaultDialog), + + # Chain 09 (reward 701: max money 60) + 9032: (TT_TIER, Start, (VisitQuest,), Any, 2005, NA, 9033, DefaultDialog), + 9033: (TT_TIER, Cont, (DeliverGagQuest, 4, ToontownBattleGlobals.SQUIRT_TRACK, 1), Same, 2005, NA, 9034, DefaultDialog), + 9034: (TT_TIER, Cont, (VisitQuest,), Same, 2003, NA, 9035, DefaultDialog), + 9035: (TT_TIER, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 10, Any), Same, ToonHQ, 701, NA, DefaultDialog), + + # Chain 10 (reward 101: +2 laff) + 9036: (TT_TIER, Start, (VisitQuest,), Any, 2003, NA, 9037, DefaultDialog), + 9037: (TT_TIER, Cont, (RecoverItemQuest, ToontownGlobals.ToontownCentral, 5, 20, 95, Any, 'type'), Same, 2003, NA, 9038, DefaultDialog), + 9038: (TT_TIER, Cont, (VisitQuest,), Same, ToonHQ, NA, 9039, DefaultDialog), + 9039: (TT_TIER, Cont, (BuildingQuest, ToontownGlobals.ToontownCentral, 1, Any, 1), Same, ToonHQ, 101, NA, DefaultDialog), + + # Chain 11 (reward 501: quest carry 3) + 9040: (TT_TIER, Start, (VisitQuest,), Any, 2002, NA, 9041, DefaultDialog), + 9041: (TT_TIER, Cont, (DeliverGagQuest, 4, ToontownBattleGlobals.THROW_TRACK, 1), Same, 2002, NA, 9042, DefaultDialog), + 9042: (TT_TIER, Cont, (VisitQuest,), Same, 2005, NA, 9043, DefaultDialog), + 9043: (TT_TIER, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 10, Any), Same, ToonHQ, 501, NA, DefaultDialog), + + # Chain 12 (reward 900: choose Toon-up OR Lure) + 9044: (TT_TIER, Start, (VisitQuest,), Any, ToonHQ, NA, 9045, DefaultDialog), + 9045: (TT_TIER, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 10, Any), Same, ToonHQ, NA, 9046, DefaultDialog), + 9046: (TT_TIER, Cont, (VisitQuest,), Same, 2003, NA, 9047, DefaultDialog), + 9047: (TT_TIER, Cont, (TrackChoiceQuest, ToontownBattleGlobals.HEAL_TRACK, ToontownBattleGlobals.LURE_TRACK), Same, ToonHQ, 900, NA, DefaultDialog), + + # Chain 13 (reward 102: +3 laff) + 9048: (TT_TIER, Start, (VisitQuest,), Any, 2005, NA, 9049, DefaultDialog), + 9049: (TT_TIER, Cont, (RecoverItemQuest, ToontownGlobals.ToontownCentral, 5, 20, 95, Any, 'type'), Same, 2005, NA, 9050, DefaultDialog), + 9050: (TT_TIER, Cont, (VisitQuest,), Same, ToonHQ, NA, 9051, DefaultDialog), + 9051: (TT_TIER, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 10, Any), Same, ToonHQ, 102, NA, DefaultDialog), + + # Chain 14 (reward 103: +4 laff) + 9052: (TT_TIER, Start, (VisitQuest,), Any, 2003, NA, 9053, DefaultDialog), + 9053: (TT_TIER, Cont, (DeliverGagQuest, 4, ToontownBattleGlobals.SQUIRT_TRACK, 2), Same, 2003, NA, 9054, DefaultDialog), + 9054: (TT_TIER, Cont, (VisitQuest,), Same, ToonHQ, NA, 9055, DefaultDialog), + 9055: (TT_TIER, Cont, (BuildingQuest, ToontownGlobals.ToontownCentral, 1, Any, 1), Same, ToonHQ, 103, NA, DefaultDialog), + + # Chain 15 (reward 104: +5 laff) - final capstone within your hard caps + 9056: (TT_TIER, Start, (VisitQuest,), Any, ToonHQ, NA, 9057, DefaultDialog), + 9057: (TT_TIER, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 10, Any), Same, ToonHQ, NA, 9058, DefaultDialog), + 9058: (TT_TIER, Cont, (VisitQuest,), Same, 2002, NA, 9059, DefaultDialog), + 9059: (TT_TIER, Cont, (RecoverItemQuest, ToontownGlobals.ToontownCentral, 5, 20, 95, Any, 'type'), Same, ToonHQ, 104, NA, DefaultDialog), + + 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, + 175, + DefaultDialog), + 150: (TT_TIER, + OBSOLETE, + (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]) +} \ No newline at end of file diff --git a/toontown/quest/QuestParser.py b/toontown/quest/QuestParser.py index 493052b..7780608 100644 --- a/toontown/quest/QuestParser.py +++ b/toontown/quest/QuestParser.py @@ -2,11 +2,15 @@ import sys import os import tokenize import copy + +import typing from direct.interval.IntervalGlobal import * from direct.directnotify import DirectNotifyGlobal from panda3d.core import * -from panda3d.otp import * from direct.showbase import DirectObject + +from libotp import CFSpeech, CFTimeout +from libotp.nametag import _constants from . import BlinkingArrows from toontown.toon import ToonHeadFrame from toontown.char import CharDNA @@ -24,11 +28,19 @@ lineDict = {} globalVarDict = {} curId = None + +if typing.TYPE_CHECKING: + from toontown.toonbase.ToonBaseGlobals import * + def init(): globalVarDict.update({'render': render, 'camera': camera, 'hidden': hidden, 'aspect2d': aspect2d, + 'bottomLeft' : base.a2dBottomLeft, + 'topLeft' : base.a2dTopLeft, + 'topRight' : base.a2dTopRight, + 'bottomRight' : base.a2dBottomRight, 'localToon': base.localAvatar, 'laffMeter': base.localAvatar.laffMeter, 'inventory': base.localAvatar.inventory, @@ -74,18 +86,18 @@ def getLineOfTokens(gen): if token[0] == tokenize.ENDMARKER: return None while token[0] != tokenize.NEWLINE and token[0] != tokenize.NL: - if token[0] in (tokenize.COMMENT, tokenize.ENCODING): + 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])) + tokens.append(-float(token[1])) nextNeg = 0 else: - tokens.append(eval(token[1])) + tokens.append(float(token[1])) elif token[0] == tokenize.STRING: - tokens.append(eval(token[1])) + tokens.append(str(token[1])) elif token[0] == tokenize.NAME: tokens.append(token[1]) else: @@ -639,7 +651,7 @@ class NPCMoviePlayer(DirectObject.DirectObject): toonId = self.toon.getDoId() avatarName = line[1] avatar = self.getVar(avatarName) - chatString = eval('TTLocalizer.' + line[2]) + chatString = getattr(TTLocalizer, line[2]) chatFlags = CFSpeech | CFTimeout quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) if extraChatFlags: @@ -666,7 +678,7 @@ class NPCMoviePlayer(DirectObject.DirectObject): quitButton = arg elif type(arg) == type(''): if len(arg) > 2 and arg[:2] == 'CF': - extraChatFlags = eval(arg) + extraChatFlags = getattr(_constants, str(arg)) else: dialogueList.append(self.getVar(arg)) else: @@ -679,7 +691,7 @@ class NPCMoviePlayer(DirectObject.DirectObject): toonId = self.toon.getDoId() avatarName = line[1] avatar = self.getVar(avatarName) - chatString = eval('TTLocalizer.' + line[2]) + chatString = getattr(TTLocalizer, line[2]) quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) return Func(avatar.setPageChat, toonId, 0, chatString, quitButton, extraChatFlags, dialogueList) @@ -687,7 +699,7 @@ class NPCMoviePlayer(DirectObject.DirectObject): lineLength = len(line) avatarName = line[1] avatar = self.getVar(avatarName) - chatString = eval('TTLocalizer.' + line[2]) + chatString = getattr(TTLocalizer, line[2]) quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) return Func(avatar.setLocalPageChat, chatString, quitButton, extraChatFlags, dialogueList) @@ -695,7 +707,7 @@ class NPCMoviePlayer(DirectObject.DirectObject): lineLength = len(line) avatarName = line[1] avatar = self.getVar(avatarName) - chatString = eval('TTLocalizer.' + line[2]) + chatString = getattr(TTLocalizer, line[2]) quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) if len(dialogueList) > 0: dialogue = dialogueList[0] @@ -710,8 +722,8 @@ class NPCMoviePlayer(DirectObject.DirectObject): toAvatarKey = line[2] toAvatar = self.getVar(toAvatarKey) localizerAvatarName = toAvatar.getName().capitalize() - toAvatarName = eval('TTLocalizer.' + localizerAvatarName) - chatString = eval('TTLocalizer.' + line[3]) + toAvatarName = getattr(TTLocalizer, localizerAvatarName) + chatString = getattr(TTLocalizer, line[3]) chatString = chatString.replace('%s', toAvatarName) quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[4:]) return Func(avatar.setLocalPageChat, chatString, quitButton, extraChatFlags, dialogueList) @@ -721,9 +733,9 @@ class NPCMoviePlayer(DirectObject.DirectObject): avatarName = line[1] avatar = self.getVar(avatarName) if self.toon.getStyle().gender == 'm': - chatString = eval('TTLocalizer.' + line[2] % 'Mickey') + chatString = getattr(TTLocalizer, (line[2] % 'Mickey').replace('"', "")) else: - chatString = eval('TTLocalizer.' + line[2] % 'Minnie') + chatString = getattr(TTLocalizer, (line[2] % 'Minnie').replace('"', "")) quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) return Func(avatar.setLocalPageChat, chatString, quitButton, extraChatFlags, dialogueList) @@ -734,11 +746,11 @@ class NPCMoviePlayer(DirectObject.DirectObject): toAvatarKey = line[2] toAvatar = self.getVar(toAvatarKey) localizerAvatarName = toAvatar.getName().capitalize() - toAvatarName = eval('TTLocalizer.' + localizerAvatarName) + toAvatarName = getattr(TTLocalizer, localizerAvatarName) if self.toon.getStyle().gender == 'm': - chatString = eval('TTLocalizer.' + line[3] % 'Mickey') + chatString = getattr(TTLocalizer, (line[3] % 'Mickey').replace('"', "")) else: - chatString = eval('TTLocalizer.' + line[3] % 'Minnie') + chatString = getattr(TTLocalizer, (line[3] % 'Minnie').replace('"', "")) chatString = chatString.replace('%s', toAvatarName) quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[4:]) return Func(avatar.setLocalPageChat, chatString, quitButton, extraChatFlags, dialogueList) @@ -855,9 +867,18 @@ class NPCMoviePlayer(DirectObject.DirectObject): def parseFunction(self, line): token, objectName, functionName = line + objectName = objectName.replace('"', "") + functionName = functionName.replace('"', "") object = self.getVar(objectName) - cfunc = compile('object' + '.' + functionName, '', 'eval') - return Func(eval(cfunc)) + # This is hacky, but it works. + if "questPage" in functionName: + if "showQuestsOnscreenTutorial" in functionName: + return Func(base.localAvatar.questPage.showQuestsOnscreenTutorial) + elif "hideQuestsOnscreenTutorial" in functionName: + return Func(base.localAvatar.questPage.hideQuestsOnscreenTutorial) + else: + cfunc = getattr(object, functionName) + return Func(cfunc) def parseAddLaffMeter(self, line): token, maxHpDelta = line @@ -986,7 +1007,7 @@ class NPCMoviePlayer(DirectObject.DirectObject): token, enable = line if enable: - def phraseSaid(phraseId): + def phraseSaid(phraseId, displayType): toontastic = 315 if phraseId == toontastic: messenger.send(DistributedBlackCatMgr.DistributedBlackCatMgr.ActivateEvent) @@ -1066,9 +1087,9 @@ class NPCMoviePlayer(DirectObject.DirectObject): return Wait(0.0) +vfs = VirtualFileSystem.getGlobalPtr() searchPath = DSearchPath() -if __debug__: - searchPath.appendDirectory(Filename('resources/phase_3/etc')) +searchPath.appendDirectory(Filename('/phase_3/etc')) scriptFile = Filename('QuestScripts.txt') found = vfs.resolveFilename(scriptFile, searchPath) if not found: diff --git a/toontown/quest/Quests.py b/toontown/quest/Quests.py index 655a25b..5134aad 100644 --- a/toontown/quest/Quests.py +++ b/toontown/quest/Quests.py @@ -17675,6 +17675,19 @@ QuestDict = { NA, TTLocalizer.QuestDialogDict[12032])} +WANT_LEGACY_QUESTS = True + +if WANT_LEGACY_QUESTS: + from toontown.quest import LegacyQuestDict + for key, value in LegacyQuestDict.QuestDict.items(): + QuestDict[key] = value + +def questExists(questId) -> bool: + try: + return int(questId) in QuestDict + except Exception: + return False + Tier2QuestsDict = {} for questId, questDesc in list(QuestDict.items()): if questDesc[QuestDictStartIndex] == Start: diff --git a/toontown/shaders/post.vert.glsl b/toontown/shaders/post.vert.glsl new file mode 100644 index 0000000..8e6ab54 --- /dev/null +++ b/toontown/shaders/post.vert.glsl @@ -0,0 +1,16 @@ +// Vertex shader for the HDR scene composite quad rendered through a +// 3D orthographic FilterManager camera. Unlike the render2dp overlay +// path (sunrays.vert.glsl), the quad lives in model-space (XZ plane, +// Y=0), so the full ModelViewProjection transform is required to map it +// to clip space correctly. +#version 130 + +uniform mat4 p3d_ModelViewProjectionMatrix; +in vec4 p3d_Vertex; +in vec2 p3d_MultiTexCoord0; +out vec2 uv; + +void main() { + gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; + uv = p3d_MultiTexCoord0; +} diff --git a/toontown/shaders/scene_post.frag.glsl b/toontown/shaders/scene_post.frag.glsl new file mode 100644 index 0000000..c509ca9 --- /dev/null +++ b/toontown/shaders/scene_post.frag.glsl @@ -0,0 +1,192 @@ +// Scene post-processing composite fragment shader. +// +// Single-pass pipeline that reads a FilterManager-captured scene and applies: +// 1. Depth-buffer-occluded screen-space god rays (Kenny Mitchell technique, +// GPU Gems 3 Ch. 13). Sky pixels (depth ≈ 1.0) let light through; +// solid geometry pixels block it, creating real geometry-cast light shafts. +// 2. Approximate single-pass bloom (bright-pass + large kernel box blur +// at multiple offsets — not physically perfect but fast and convincing). +// 3. ACES filmic tonemapping with per-zone exposure adjustment. +// +// Uniforms set by OutdoorLighting._setupPostProcess(): +// sceneColor – RGBA scene texture (float16 or RGBA8 offscreen; exposure+ACES below) +// sceneDepth – depth texture matching sceneColor dimensions +// sunScreenPos – sun position in [0,1] screen UV space +// rayColor – god-ray tint (RGBA) +// rayIntensity – master ray strength (0 = off) +// bloomIntensity – bloom strength (0 = off) +// bloomThreshold – luminance threshold for bright-pass +// exposure – scene exposure scalar (default 1.0) +// tonemapEnabled – 0 = bypass tonemap (linear output), 1 = ACES +// vignetteStrength – 0 = off, 0.25 = subtle, 1 = strong +// time – animation seconds (unused here; reserved for shimmer) +// texelSize – vec2(1/width, 1/height) for blur offsets +#version 130 + +uniform sampler2D sceneColor; +uniform sampler2D sceneDepth; +uniform vec2 sunScreenPos; +uniform vec4 rayColor; +uniform float rayIntensity; +// Bloom is temporarily disabled in code to avoid a Panda3D shader input +// assertion on some drivers; keep the effect path available for later. +uniform float exposure; +uniform float tonemapEnabled; +uniform float vignetteStrength; +uniform float time; +uniform vec2 texelSize; + +in vec2 uv; +out vec4 fragColor; + +// ── ACES filmic tonemapper ─────────────────────────────────────────────────── +// Fitted curve by Krzysztof Narkowicz (2015). Very close to the full ACES +// reference at a fraction of the cost. +vec3 acesTonemap(vec3 x) { + const float a = 2.51; + const float b = 0.03; + const float c = 2.43; + const float d = 0.59; + const float e = 0.14; + return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0.0, 1.0); +} + +// ── Reinhard (per-channel) ─────────────────────────────────────────────────── +vec3 reinhardTonemap(vec3 x) { + return x / (1.0 + x); +} + +// ── Luminance helper ───────────────────────────────────────────────────────── +float luminance(vec3 c) { + return dot(c, vec3(0.2126, 0.7152, 0.0722)); +} + +// ── Screen-space depth-occluded god rays ───────────────────────────────────── +// +// Algorithm: march from the current pixel toward the sun position in screen +// space (NUM_SAMPLES steps). At each step sample the depth buffer. +// depth == 1.0 (or very close) → sky pixel → sun is visible → accumulate +// depth < DEPTH_THRESHOLD → geometry pixel → occluded → skip +// +// The accumulated value is weighted by an exponential decay so samples nearer +// the sun contribute more. The classic GPU Gems 3 weighting applies. +vec3 godRays(vec2 pixelUV, float intensity) { + if (intensity <= 0.001) return vec3(0.0); + + const int NUM_SAMPLES = 96; + const float DECAY = 0.966; + const float DENSITY = 0.84; + const float WEIGHT = 0.45; + const float EXPOSURE_RAY = 0.16; + const float DEPTH_THRESHOLD = 0.9998; // sky depth threshold + + // Cull when the sun is completely off-screen to avoid aliasing artefacts. + vec2 sunEdgeDist = min(sunScreenPos, 1.0 - sunScreenPos); + float edgeFade = smoothstep(0.0, 0.08, min(sunEdgeDist.x, sunEdgeDist.y)); + if (edgeFade <= 0.0) return vec3(0.0); + + vec2 delta = (pixelUV - sunScreenPos) * (DENSITY / float(NUM_SAMPLES)); + vec2 sampleUV = pixelUV; + float illum = 0.0; + float decay = 1.0; + + for (int i = 0; i < NUM_SAMPLES; ++i) { + sampleUV -= delta; + // Clamp so we don't sample outside the texture. + vec2 cUV = clamp(sampleUV, vec2(0.001), vec2(0.999)); + float d = texture(sceneDepth, cUV).r; + // Sky pixels (d ≥ DEPTH_THRESHOLD) are unoccluded → contribute. + float sky = step(DEPTH_THRESHOLD, d); + illum += sky * decay * WEIGHT; + decay *= DECAY; + } + illum *= EXPOSURE_RAY * intensity * edgeFade; + + return rayColor.rgb * illum; +} + +// ── Single-pass approximate bloom ─────────────────────────────────────────── +// +// Extracts bright pixels then blurs with a two-ring sample pattern (Poisson +// disc approximation). Not as smooth as multi-pass Gaussian but avoids the +// need for ping-pong buffers, keeping us in one FilterManager pass. +vec3 bloom(vec2 pixUV, float threshold, float intensity) { + if (intensity <= 0.001) return vec3(0.0); + + vec3 acc = vec3(0.0); + float total = 0.0; + + // Two rings: inner (4 samples) + outer (8 samples) + // Offsets are in texel units; scale drives blur radius. + float blurRadius = mix(3.0, 9.0, intensity); + + vec2 offsets[12]; + // Inner ring + offsets[0] = vec2( 1.0, 0.0); + offsets[1] = vec2(-1.0, 0.0); + offsets[2] = vec2( 0.0, 1.0); + offsets[3] = vec2( 0.0, -1.0); + // Mid ring + offsets[4] = vec2( 1.5, 1.5); + offsets[5] = vec2(-1.5, 1.5); + offsets[6] = vec2( 1.5, -1.5); + offsets[7] = vec2(-1.5, -1.5); + // Outer ring + offsets[8] = vec2( 3.0, 0.0); + offsets[9] = vec2(-3.0, 0.0); + offsets[10] = vec2( 0.0, 3.0); + offsets[11] = vec2( 0.0, -3.0); + + float weights[12]; + weights[0] = 1.00; weights[1] = 1.00; + weights[2] = 1.00; weights[3] = 1.00; + weights[4] = 0.70; weights[5] = 0.70; + weights[6] = 0.70; weights[7] = 0.70; + weights[8] = 0.35; weights[9] = 0.35; + weights[10] = 0.35; weights[11] = 0.35; + + for (int i = 0; i < 12; ++i) { + vec2 sUV = pixUV + offsets[i] * texelSize * blurRadius; + vec3 col = texture(sceneColor, sUV).rgb; + float lum = luminance(col); + float bright = max(0.0, lum - threshold); + acc += col * bright * weights[i]; + total += weights[i]; + } + if (total > 0.0) acc /= total; + + return acc * intensity * 1.4; +} + +// ── Vignette ──────────────────────────────────────────────────────────────── +float vignette(vec2 u, float strength) { + vec2 d = u - 0.5; + return 1.0 - dot(d, d) * strength * 3.2; +} + +// ── Main ───────────────────────────────────────────────────────────────────── +void main() { + vec4 sceneRGBA = texture(sceneColor, uv); + vec3 col = sceneRGBA.rgb * exposure; + + // ── God rays ───────────────────────────────────────────────────────────── + col += godRays(uv, rayIntensity); + + // ── Bloom ──────────────────────────────────────────────────────────────── + // Disabled for now (see note above). + col += bloom(uv, 1.0, 0.0); + + // ── Tonemapping ────────────────────────────────────────────────────────── + if (tonemapEnabled > 0.5) { + col = acesTonemap(col); + } else { + col = clamp(col, 0.0, 1.0); + } + + // ── Vignette ───────────────────────────────────────────────────────────── + if (vignetteStrength > 0.001) { + col *= clamp(vignette(uv, vignetteStrength), 0.0, 1.0); + } + + fragColor = vec4(col, sceneRGBA.a); +} diff --git a/toontown/shaders/sky.frag.glsl b/toontown/shaders/sky.frag.glsl new file mode 100644 index 0000000..170e78d --- /dev/null +++ b/toontown/shaders/sky.frag.glsl @@ -0,0 +1,440 @@ +// Procedural physically-based sky fragment shader – MASSIVE REWRITE +// +// Implements (new and improved): +// • Rayleigh + Mie scattering – accurate wavelength-dependent blue-sky gradient +// • Wide sunset/sunrise horizon corona (orange-pink band near horizon) +// • Ozone absorption – cyan/yellow sky transition near sunset +// • Horizon haze / atmospheric extinction with turbidity control +// • VISIBLE SUN DISC with: +// – Physical angular radius (~0.27°) +// – Limb darkening (edges slightly darker than centre) +// – Colour shifts noon→white-yellow, sunset→deep orange-red +// – Multi-layer exponential corona / aureole +// – Subtle vertical lens-flare streak +// – BLINDING GLARE when staring directly into the sun (chromatic aberration glow) +// • Volumetric-looking FBM cumulus clouds with domain warping: +// – Two-pass domain warp (organic, non-repetitive shapes) +// – Multi-layer depth sampling (distinct top/middle/base layers) +// – Sun back-lighting (silver lining on edges facing sun) +// – Sunset orange-pink underbelly glow +// – Moon-lit night-time clouds (cool blue-grey) +// – Per-zone coverage, speed, sharpness +// • Twinkling star field: +// – Per-star unique twinkle frequency & phase +// – Four spectral classes: blue-white / white / yellow-white / warm +// – Occasional "sparkle" diffraction cross on bright stars +// – Fades behind clouds and near the sun +// • MOON disc with: +// – Surface noise detail (craters) +// – Limb darkening +// – Multi-layer corona glow +// – Cloud occlusion +// • Zone colour scale (skyScale) applied to final output +// • Reinhard tonemapping + gamma lift for natural HDR-to-LDR +// +// Uniforms set by ProceduralSky.update(): +// sunDir – world-space direction TOWARD the sun (normalised) +// sunColor – key light colour; used for Mie glow tint +// zenithColor – deep sky colour at zenith (zone) +// horizonColor – sky colour at horizon (zone) +// fogColor – atmospheric haze/fog tint (zone) +// cloudCoverage – 0.0 (clear) … 1.0 (overcast) (zone) +// cloudSpeed – cloud animation multiplier (zone) +// cloudSharpness – edge sharpness 0.0 (fluffy) … 1.0 (sharp) (zone) +// turbidity – Mie strength 1.0 (crisp) … 8.0 (hazy) (zone) +// starBrightness – star intensity; 0 in day, up to 1 at night (zone) +// moonEnabled – 1.0 = draw a moon disc +// moonDir – world-space direction toward moon +// moonColor – moon disc tint +// time – animation seconds (drives cloud drift + star twinkle) +// skyScale – vec4 colour multiplier (zone tint) +// sunDiscEnabled – 1.0 = draw visible sun disc (default 1) +// sunBlindStrength – 0.0–1.0 blinding glare when staring into sun +// sunWorldElev – sun direction Z in *world* space [-1..1] (time-of-day); +// separate from sunDir so atmosphere does not swim when the camera tilts +#version 130 + +// ── Atmosphere uniforms ──────────────────────────────────────────────────── +uniform vec3 sunDir; +uniform float sunWorldElev; +uniform vec4 sunColor; +uniform vec3 zenithColor; +uniform vec3 horizonColor; +uniform vec3 fogColor; +// ── Cloud uniforms ───────────────────────────────────────────────────────── +uniform float cloudCoverage; +uniform float cloudSpeed; +uniform float cloudSharpness; +// ── Scattering ──────────────────────────────────────────────────────────── +uniform float turbidity; +// ── Night sky ───────────────────────────────────────────────────────────── +uniform float starBrightness; +uniform float moonEnabled; +uniform vec3 moonDir; +uniform vec4 moonColor; +// ── Animation ───────────────────────────────────────────────────────────── +uniform float time; +uniform vec4 skyScale; +// ── New features ────────────────────────────────────────────────────────── +uniform float sunDiscEnabled; // 1.0 = render sun disc + corona +uniform float sunBlindStrength; // 0–1 glare when staring at sun + +in vec3 vDir; +in vec2 vUV; +out vec4 fragColor; + +// ───────────────────────────────────────────────────────────────────────── +// Noise / hash utilities +// ───────────────────────────────────────────────────────────────────────── + +float _hash(vec2 p) { + p = fract(p * vec2(127.1, 311.7)); + p += dot(p, p + 19.19); + return fract(p.x * p.y); +} + +float _vnoise(vec2 p) { + vec2 i = floor(p); + vec2 f = fract(p); + vec2 u = f * f * (3.0 - 2.0 * f); + return mix( + mix(_hash(i), _hash(i + vec2(1.0, 0.0)), u.x), + mix(_hash(i + vec2(0.0, 1.0)), _hash(i + vec2(1.0, 1.0)), u.x), + u.y + ); +} + +// 8-octave FBM with per-octave rotation (breaks axis-aligned repetition) +float _fbm(vec2 p) { + float v = 0.0; + float amp = 0.5; + mat2 rot = mat2(1.6, 1.2, -1.2, 1.6); + for (int i = 0; i < 8; ++i) { + v += amp * _vnoise(p); + p = rot * p * 2.1; + amp *= 0.46; + } + return v; +} + +// ───────────────────────────────────────────────────────────────────────── +// Phase functions +// ───────────────────────────────────────────────────────────────────────── + +float _mie(float cosA, float g) { + float g2 = g * g; + return (1.0 - g2) / pow(max(1.0e-4, 1.0 + g2 - 2.0 * g * cosA), 1.5) * 0.25; +} + +float _rayleigh(float cosA) { + return 0.75 * (1.0 + cosA * cosA); +} + +// ───────────────────────────────────────────────────────────────────────── +// Main +// ───────────────────────────────────────────────────────────────────────── + +void main() { + vec3 dir = normalize(vDir); + float elev = dir.z; + float elevAbs = abs(elev); + float cosTheta = dot(dir, sunDir); + // World-space sun height (stable when camera pitches/yaws); sunDir is camera-space for dots. + float sunElevW = sunWorldElev; + // sunPower: 0 when sun is on or below horizon, rises to 1 near zenith + float sunPower = clamp(sunElevW * 3.5 + 0.28, 0.0, 1.0); + + // ── Rayleigh scattering ────────────────────────────────────────────── + // Wavelength-dependent (shorter λ = blue scatters more) + vec3 rayleigh_wl = vec3(0.26, 0.50, 1.00); + float rayleighPhase = _rayleigh(cosTheta); + vec3 rayleighCol = rayleigh_wl * rayleighPhase * mix(0.28, 1.05, sunPower); + + // ── Mie scattering (turbidity-driven forward halo) ─────────────────── + float g = max(0.58, 0.84 - turbidity * 0.020); + float mieStr = turbidity * 0.13; + float mieGlow = _mie(cosTheta, g) * mieStr; + vec3 mieCol = sunColor.rgb * mieGlow; + + // ── Wide horizon corona at sunset/sunrise ──────────────────────────── + // Horizontal dot: measures how closely dir aligns with sun's azimuth at horizon + vec2 sunAz = vec2(sunDir.x, sunDir.y); + float sunAzLen = max(0.001, length(sunAz)); + float horizDot = dot(vec2(dir.x, dir.y), sunAz / sunAzLen); + float horizBand = pow(max(0.0, horizDot), 3.5) + * max(0.0, 1.0 - abs(sunElevW) * 3.8) // only near horizon + * (1.0 - abs(elev) * 3.0) // fade away from horizon line + * 0.70; + vec3 horizGlowCol = mix( + vec3(1.00, 0.42, 0.06), // deep orange at low elevation + vec3(1.00, 0.80, 0.42), // golden higher up + clamp(sunPower * 1.5, 0.0, 1.0) + ) * horizBand; + + // ── Ozone absorption ───────────────────────────────────────────────── + float ozone = max(0.0, 1.0 - elevAbs * 1.55); + vec3 ozoneCol = vec3(0.00, 0.13, 0.22) * ozone * sunPower; + + // ── Sky gradient (zenith → horizon) ────────────────────────────────── + float horizonT = pow(clamp(elev * 1.25 + 0.14, 0.0, 1.0), 0.50); + vec3 gradCol = mix(horizonColor, zenithColor, horizonT); + + // Sunset/sunrise band: orange-pink near horizon, purple higher up + float sunsetBand = max(0.0, 1.0 - abs(sunElevW) * 2.2) * (1.0 - horizonT * 0.75); + vec3 sunsetTint = mix( + vec3(1.00, 0.38, 0.05), // near-horizon orange + vec3(0.55, 0.25, 0.72), // purple higher + horizonT + ) * sunsetBand * 0.62; + gradCol += sunsetTint; + + // Assemble base sky + vec3 sky = gradCol + + rayleighCol * 0.42 + + mieCol + + ozoneCol + + horizGlowCol; + + // ── Horizon haze (atmospheric extinction) ──────────────────────────── + float hazeT = exp(-max(0.0, elev) * 5.2 * turbidity * 0.27); + sky = mix(sky, fogColor, hazeT * 0.52); + + // ── Below horizon: fade to dark fog ────────────────────────────────── + if (elev < 0.0) { + float below = clamp(-elev * 9.5, 0.0, 1.0); + sky = mix(sky, fogColor * 0.48, below); + } + + // ───────────────────────────────────────────────────────────────────── + // Volumetric clouds + // ───────────────────────────────────────────────────────────────────── + + float cloudAlpha = 0.0; + vec3 cloudRGB = vec3(1.0); + + if (cloudCoverage > 0.02 && elev > -0.07) { + // Perspective projection onto cloud layer at ~1 km altitude + float layerScale = 1.0 / max(0.05, elev + 0.05); + vec2 cloudUV = vec2(dir.x, dir.y) * layerScale * 0.36 + + vec2(time * cloudSpeed * 0.00115, time * cloudSpeed * 0.00045); + + // ── Domain warping pass 1 (large-scale organic distortion) ─────── + vec2 warp1 = vec2( + _fbm(cloudUV * 1.55), + _fbm(cloudUV * 1.55 + vec2(5.20, 1.30)) + ) * 0.32; + + // ── Domain warping pass 2 (medium-scale detail) ────────────────── + vec2 warp2 = vec2( + _fbm(cloudUV * 0.82 + vec2(1.70, 9.20)), + _fbm(cloudUV * 0.82 + vec2(8.30, 2.80)) + ) * 0.14; + + vec2 warpedUV = cloudUV + warp1 + warp2; + + // Primary cloud density field + float density = _fbm(warpedUV * 2.35); + + // Coverage → threshold + float threshold = 1.0 - cloudCoverage * 0.76; + float raw = density - threshold; + float edgeWidth = mix(0.32, 0.045, cloudSharpness); + float shaped = smoothstep(0.0, edgeWidth, raw); + + // Horizon fade (perspective stretch makes low-angle clouds blur badly) + shaped *= smoothstep(-0.04, 0.18, elev); + + // ── Multi-layer depth sampling for 3-D cloud body feel ────────── + float densityMid = _fbm(warpedUV * 4.00 + vec2(1.70, 3.10)); + float densityFine = _fbm(warpedUV * 8.50 + vec2(-2.30, 0.80)); + // volDepth: 0 = outer edge, 1 = deep interior + float volDepth = clamp(densityMid * 0.45 + densityFine * 0.18, 0.0, 1.0); + + // ── Cloud illumination ─────────────────────────────────────────── + float sunDot = max(0.0, dot(dir, sunDir)); + float shadowing = clamp(1.0 - shaped * 0.68, 0.16, 1.0); + + // Top surface: brightly lit by direct sun + vec3 litTop = mix(vec3(0.96, 0.97, 1.00), sunColor.rgb * 1.20, 0.20); + + // Cloud base: deeper interior → darker grey-blue shadow + float depthSh = mix(0.36, 0.62, volDepth); + vec3 litBase = litTop * vec3(depthSh * 0.88, depthSh * 0.93, depthSh * 1.04); + + // Sunset underbelly: orange-pink glow when sun is near horizon + float sunsetC = max(0.0, 1.0 - abs(sunElevW) * 4.0) * sunPower; + vec3 sunsetBelly = mix( + vec3(1.0, 0.55, 0.22), + vec3(1.0, 0.78, 0.52), + clamp(sunDot, 0.0, 1.0) + ) * sunsetC * 0.75; + litBase += sunsetBelly; + + // Second FBM sample for vertical shading variation + float baseShade = _fbm(warpedUV * 2.35 + vec2(0.30, 0.15)) * 0.72 + 0.28; + cloudRGB = mix(litBase, litTop, baseShade * shadowing); + + // Silver lining: bright backlit halo on sun-facing cloud edges + float silverEdge = smoothstep(edgeWidth * 0.55, 0.0, raw) * sunDot; + cloudRGB += vec3(0.72, 0.64, 0.46) * silverEdge * sunPower * 0.70; + + // Night / moon-lit clouds (cool dim blue-grey) + if (moonEnabled > 0.5 && sunPower < 0.30) { + float mnFade = clamp((0.30 - sunPower) * 4.0, 0.0, 1.0); + float mnDot = max(0.0, dot(dir, normalize(moonDir))); + vec3 mnLight = vec3(0.35, 0.42, 0.60) * mnDot * 0.38; + float nDepth = mix(0.06, 0.52, shaped); + cloudRGB = mix(cloudRGB, vec3(0.05, 0.07, 0.14) + mnLight * shaped, + mnFade * nDepth); + } + + cloudAlpha = shaped; + } + + sky = mix(sky, cloudRGB, cloudAlpha); + + // ───────────────────────────────────────────────────────────────────── + // Twinkling star field + // ───────────────────────────────────────────────────────────────────── + + if (starBrightness > 0.004 && elev > 0.02) { + // Quantise direction → star cells (each has at most 1 star) + vec3 snap = floor(dir * 210.0) / 210.0; + float seed = _hash(snap.xy * vec2(43.0, 127.0) + snap.z * 59.0); + + // Star brightness / size (varies per cell) + float szSeed = _hash(snap.yx * vec2(71.0, 23.0) + snap.z * 17.0); + float exponent = mix(430.0, 680.0, szSeed); + float rawBrt = pow(seed, exponent) * mix(2.4, 5.2, szSeed); + + // ── Twinkle: per-star unique frequency and phase ───────────────── + float twSeed = _hash(snap.xy * 33.0 + snap.z * 71.0); + float twFreq = mix(0.35, 4.0, twSeed); + float twPhase = twSeed * 6.28318530; + // Primary twinkle + float twinkle = 0.62 + 0.38 * sin(time * twFreq + twPhase); + // Secondary high-frequency shimmer on bright stars + float shimmer = 1.0 + 0.15 * sin(time * twFreq * 3.3 + twPhase * 1.7); + float star = rawBrt * twinkle * shimmer; + + // ── Star spectral class ────────────────────────────────────────── + float colSeed = _hash(snap.yz * vec2(37.0, 53.0)); + vec3 starCol; + if (colSeed < 0.22) starCol = vec3(0.76, 0.84, 1.00); // O/B blue-white + else if (colSeed < 0.48) starCol = vec3(1.00, 1.00, 0.94); // A/F white + else if (colSeed < 0.74) starCol = vec3(1.00, 0.95, 0.70); // G yellow-white + else starCol = vec3(1.00, 0.75, 0.55); // K/M warm orange + + // Dim near sun's position in sky + float nearSun = max(0.0, dot(dir, sunDir)); + star *= max(0.0, 1.0 - nearSun * nearSun * 4.0); + + // Fade behind clouds + star *= max(0.0, 1.0 - cloudAlpha * 1.2); + + sky += starCol * star * starBrightness; + + // ── Occasional bright "sparkle" with diffraction cross ─────────── + if (seed > 0.9982) { + float sparkle = (seed - 0.9982) / 0.0018; + sparkle = sparkle * sparkle * twinkle * twinkle; + // Cross arms: exponential falloff from snap position + float crossW = 0.0055; + float arm_h = exp(-abs(dir.x - snap.x) / crossW); + float arm_v = exp(-abs(dir.y - snap.y) / crossW); + float cross2 = (arm_h + arm_v) * 0.5; + sky += starCol * cross2 * sparkle * starBrightness * 0.50 + * max(0.0, 1.0 - cloudAlpha * 1.5); + } + } + + // ───────────────────────────────────────────────────────────────────── + // Sun disc + corona + blinding glare + // ───────────────────────────────────────────────────────────────────── + + if (sunDiscEnabled > 0.5 && sunElevW > -0.14) { + float angDist = acos(clamp(cosTheta, -1.0, 1.0)); + float sunR = 0.0048; // angular radius of sun disc (~0.275°) + + // ── Solar disc with limb darkening ─────────────────────────────── + float limbT = clamp(1.0 - angDist / sunR, 0.0, 1.0); + float limb = smoothstep(0.0, 1.0, limbT) * step(angDist, sunR * 1.30); + float limbDrk = mix(0.68, 1.0, limbT); // edges 32% darker than centre + + // Colour: white-yellow at noon → deep orange-red at horizon + float sunsetT = clamp(1.0 - sunElevW * 5.5, 0.0, 1.0); + vec3 discCol = mix( + vec3(1.00, 0.97, 0.82) * 5.0, // noon: brilliant white-yellow + vec3(1.00, 0.44, 0.05) * 2.8, // sunset: deep orange-red + sunsetT + ) * limbDrk; + + // ── Multi-layer corona / aureole ───────────────────────────────── + float c1 = exp(-angDist * 340.0) * 1.60; + float c2 = exp(-angDist * 95.0) * 0.70; + float c3 = exp(-angDist * 30.0) * 0.32; + float c4 = exp(-angDist * 8.5) * 0.12; + vec3 coronaCol = sunColor.rgb * (c1 + c2 + c3 + c4) * sunPower; + + // ── Vertical diffraction streak ────────────────────────────────── + float streak = exp(-abs(dir.z - sunDir.z) * 90.0) + * exp(-max(0.0, 1.0 - cosTheta) * 150.0) + * 0.28 * sunPower; + vec3 streakCol = sunColor.rgb * streak; + + // ── Blinding glare + chromatic aberration ──────────────────────── + float blindAmt = pow(max(0.0, cosTheta), 55.0) * sunBlindStrength * sunPower; + // Slightly wider red channel for chromatic effect + vec3 blindCol = vec3( + pow(max(0.0, cosTheta), 38.0) * sunBlindStrength * sunPower * 1.35, + blindAmt * 0.90, + blindAmt * 0.65 + ); + + // ── Visibility: blocked by clouds, clipped below horizon ───────── + float sunVis = (1.0 - cloudAlpha * 0.93) + * clamp((sunElevW + 0.12) * 7.5, 0.0, 1.0); + + sky += (discCol * limb + coronaCol + streakCol + blindCol) * sunVis; + } + + // ───────────────────────────────────────────────────────────────────── + // Moon disc + surface detail + corona + // ───────────────────────────────────────────────────────────────────── + + if (moonEnabled > 0.5) { + vec3 mDir = normalize(moonDir); + float moonDot = dot(dir, mDir); + float moonR = 0.9990; // cos(~2.56°) + float moonDisc = smoothstep(moonR, moonR + 0.0008, moonDot); + + // Surface noise → subtle crater/mare variation + vec3 mTang = normalize(dir - mDir * moonDot); + float mDetail = _vnoise(vec2(mTang.x * 550.0 + 30.0, + mTang.z * 550.0 + 70.0)) * 0.18 + 0.82; + + // Limb darkening + float mLimb = mix(0.65, 1.0, + clamp(1.0 - max(0.0, 1.0 - moonDot) * 3.0, 0.0, 1.0)); + vec3 moonSurf = vec3(0.88, 0.88, 0.80) * mDetail * mLimb; + + // Multi-layer glow + float mH1 = exp(-max(0.0, 1.0 - moonDot) * 110.0) * 0.30; + float mH2 = exp(-max(0.0, 1.0 - moonDot) * 24.0) * 0.09; + float mH3 = exp(-max(0.0, 1.0 - moonDot) * 6.0) * 0.03; + vec3 moonGlow = moonColor.rgb * (mH1 + mH2 + mH3) * 0.85; + + float moonVis = max(0.0, 1.0 - cloudAlpha * 0.90); + sky = mix(sky, moonSurf * 1.55, moonDisc * moonVis); + sky += moonGlow * moonVis; + } + + // ── Zone colour scale ───────────────────────────────────────────────── + sky *= skyScale.rgb; + + // ── Tonemapping: Reinhard + mild gamma lift ─────────────────────────── + sky = sky / (sky + vec3(0.72)); // soft knee HDR clamp + sky = pow(clamp(sky, 0.0, 1.0), vec3(1.0 / 1.15)); // slight gamma lift + + fragColor = vec4(sky, 1.0); +} diff --git a/toontown/shaders/sky.vert.glsl b/toontown/shaders/sky.vert.glsl new file mode 100644 index 0000000..74dc354 --- /dev/null +++ b/toontown/shaders/sky.vert.glsl @@ -0,0 +1,32 @@ +// Procedural sky sphere vertex shader. +// +// The sky is rendered on a large sphere (~950 unit radius) that follows the +// camera. Because the sphere is centred at the camera in model space, every +// vertex position is already a world-space ray direction. The fragment shader +// uses that direction to compute atmospheric scattering colour and clouds. +// +// Panda3D coordinate convention: Y = forward, Z = up, X = right. +#version 130 + +in vec4 p3d_Vertex; +uniform mat4 p3d_ModelViewProjectionMatrix; + +out vec3 vDir; // unnormalised model-space direction (normalised in frag) +out vec2 vUV; // model-space polar UV for cloud tiling + +void main() { + gl_Position = (p3d_ModelViewProjectionMatrix * p3d_Vertex).xyww; + // xyww trick forces depth to 1.0 (far clip) in NDC so sky is always behind + // geometry — no depth write needed, but this makes the depth test pass even + // without disabling depth write on the NodePath. + + vDir = p3d_Vertex.xyz; + + // Spherical UV: longitude (azimuth) on X, latitude (elevation) on Y. + // Used for cloud layer texture-coordinate calculation in the fragment shader. + float len = length(p3d_Vertex.xyz); + vec3 d = p3d_Vertex.xyz / max(len, 0.001); + float phi = atan(d.x, d.y); // azimuth [-π, π] + float theta = asin(clamp(d.z, -1.0, 1.0)); // elevation [-π/2, π/2] + vUV = vec2(phi / 6.28318530 + 0.5, theta / 3.14159265 + 0.5); +} diff --git a/toontown/shaders/sunrays.frag.glsl b/toontown/shaders/sunrays.frag.glsl new file mode 100644 index 0000000..a5e492c --- /dev/null +++ b/toontown/shaders/sunrays.frag.glsl @@ -0,0 +1,65 @@ +#version 120 + +// Sun rays / god rays fragment shader +// Creates volumetric light scattering effect + +uniform vec2 sunPos; +uniform vec4 rayColor; +uniform float rayIntensity; +uniform float time; +uniform float aspectRatio; + +varying vec2 texcoord; + +float hash(float n) { + return fract(sin(n) * 43758.5453); +} + +float noise(vec2 x) { + vec2 p = floor(x); + vec2 f = fract(x); + f = f * f * (3.0 - 2.0 * f); + float n = p.x + p.y * 57.0; + return mix(mix(hash(n + 0.0), hash(n + 1.0), f.x), + mix(hash(n + 57.0), hash(n + 58.0), f.x), f.y); +} + +float fbm(vec2 p) { + float f = 0.0; + f += 0.5000 * noise(p); p *= 2.02; + f += 0.2500 * noise(p); p *= 2.03; + f += 0.1250 * noise(p); p *= 2.01; + f += 0.0625 * noise(p); + return f / 0.9375; +} + +void main() { + vec2 uv = texcoord; + + // Adjust for aspect ratio + uv.x *= aspectRatio; + vec2 lightPos = sunPos; + lightPos.x *= aspectRatio; + + // Vector from pixel to light source in screen space + vec2 delta = lightPos - uv; + float dist = length(delta); + + // IMPORTANT: + // This overlay is rendered on render2dp and does not necessarily have a + // scene texture bound to p3d_Texture0. Sampling an unbound texture can + // read undefined GPU memory (often seen as RGB flicker). + // + // Instead, generate a stable radial scattering term without sampling. + float falloff = clamp(1.0 - dist, 0.0, 1.0); + falloff = pow(falloff, 2.2); + float shaft = falloff * (0.35 + 0.65 * falloff); + + // Add subtle noise for atmospheric effect (animated very slowly). + float n = fbm(uv * 3.0 + time * 0.05) * 0.05; + shaft = clamp(shaft + n, 0.0, 1.0); + + vec4 finalColor = vec4(rayColor.rgb * shaft, clamp(rayIntensity * shaft, 0.0, 1.0)); + + gl_FragColor = finalColor; +} \ No newline at end of file diff --git a/toontown/shaders/sunrays.vert.glsl b/toontown/shaders/sunrays.vert.glsl new file mode 100644 index 0000000..361908c --- /dev/null +++ b/toontown/shaders/sunrays.vert.glsl @@ -0,0 +1,14 @@ +#version 120 + +// Simple sunrays vertex shader +// Renders full-screen quad for sun shaft effect + +attribute vec4 p3d_Vertex; +attribute vec2 p3d_MultiTexCoord0; + +varying vec2 texcoord; + +void main() { + gl_Position = p3d_Vertex; + texcoord = p3d_MultiTexCoord0; +} \ No newline at end of file diff --git a/toontown/shaders/water.frag.glsl b/toontown/shaders/water.frag.glsl new file mode 100644 index 0000000..622051a --- /dev/null +++ b/toontown/shaders/water.frag.glsl @@ -0,0 +1,95 @@ +#version 120 + +// Water fragment shader with reflection, refraction, and Fresnel effect + +uniform vec4 waterColor; +uniform float time; +uniform float waveHeight; +uniform float clarity; +uniform float reflectivity; +uniform sampler2D reflectionTex; +uniform sampler2D p3d_Texture0; + +varying vec3 normal; +varying vec3 worldPos; +varying vec2 texcoord; +varying float depth; + +// Fresnel reflectance approximation +float fresnel(vec3 incident, vec3 normal, float power) { + float cosine = dot(-incident, normal); + cosine = clamp(cosine, 0.0, 1.0); + return pow(1.0 - cosine, power); +} + +// Simple noise for water caustics +float hash(float n) { + return fract(sin(n) * 43758.5453); +} + +float noise(vec2 x) { + vec2 p = floor(x); + vec2 f = fract(x); + f = f * f * (3.0 - 2.0 * f); + float n = p.x + p.y * 57.0; + return mix(mix(hash(n + 0.0), hash(n + 1.0), f.x), + mix(hash(n + 57.0), hash(n + 58.0), f.x), f.y); +} + +float fbm(vec2 p) { + float f = 0.0; + f += 0.5000 * noise(p); p *= 2.02; + f += 0.2500 * noise(p); p *= 2.03; + f += 0.1250 * noise(p); p *= 2.01; + f += 0.0625 * noise(p); + return f / 0.9375; +} + +void main() { + // Calculate reflection coordinates + vec3 viewDir = normalize(-worldPos); + vec3 reflectDir = reflect(viewDir, normal); + + // Convert reflection direction to texture coordinates + vec2 reflCoord = 0.5 * reflectDir.xy / reflectDir.z + 0.5; + reflCoord = clamp(reflCoord, 0.0, 1.0); + + // Sample reflection texture + vec4 reflection = texture2D(reflectionTex, reflCoord); + + // Calculate Fresnel term + float fresnelTerm = fresnel(viewDir, normal, 5.0); + fresnelTerm = mix(0.02, 0.8, fresnelTerm); + + // Base water color with depth attenuation + vec4 baseColor = waterColor; + float depthFactor = clamp(1.0 - depth * 0.5, 0.1, 1.0); + baseColor.a *= depthFactor * clarity; + + // Generate caustics pattern + vec2 causticUV = worldPos.xz * 0.1 + time * 0.05; + float caustics = fbm(causticUV) * 0.3 + 0.7; + + // Combine reflection and base color + vec4 finalColor = mix(baseColor, reflection, fresnelTerm * reflectivity); + + // Apply caustics to underwater areas + finalColor.rgb *= mix(1.0, caustics, 1.0 - fresnelTerm); + + // Add wave foam based on slope + float slope = 1.0 - abs(normal.y); + float foam = smoothstep(0.3, 0.5, slope * waveHeight * 10.0); + + // Foam noise + vec2 foamUV = worldPos.xz * 0.5 + time * 0.2; + float foamNoise = fbm(foamUV); + foam *= foamNoise; + + // Add white foam + finalColor.rgb = mix(finalColor.rgb, vec3(1.0), foam * 0.3); + + // Final alpha based on depth and clarity + finalColor.a = mix(baseColor.a, 1.0, fresnelTerm * 0.5); + + gl_FragColor = finalColor; +} \ No newline at end of file diff --git a/toontown/shaders/water.vert.glsl b/toontown/shaders/water.vert.glsl new file mode 100644 index 0000000..352b243 --- /dev/null +++ b/toontown/shaders/water.vert.glsl @@ -0,0 +1,64 @@ +#version 120 + +// Water vertex shader with wave animation + +attribute vec4 p3d_Vertex; +attribute vec3 p3d_Normal; +attribute vec2 p3d_MultiTexCoord0; + +uniform mat4 p3d_ModelViewProjectionMatrix; +uniform mat4 p3d_ModelMatrix; +uniform mat3 p3d_NormalMatrix; + +uniform float time; +uniform float waveHeight; +uniform float waveSpeed; +uniform vec4 waterColor; +uniform sampler2D reflectionTex; + +varying vec3 normal; +varying vec3 worldPos; +varying vec2 texcoord; +varying vec4 reflectionColor; +varying float depth; + +// Simple sine wave function +float wave(vec2 position, float frequency, float speed) { + return sin(dot(position, vec2(frequency, frequency * 1.7)) + time * speed); +} + +void main() { + vec4 vertex = p3d_Vertex; + + // Generate wave displacement + vec2 pos = vertex.xz; + float height = 0.0; + + // Multiple octaves for realistic waves + height += wave(pos, 0.1, waveSpeed) * 0.5; + height += wave(pos, 0.2, waveSpeed * 1.3) * 0.25; + height += wave(pos, 0.4, waveSpeed * 1.7) * 0.125; + height += wave(pos, 0.8, waveSpeed * 2.1) * 0.0625; + + // Apply wave height + vertex.y += height * waveHeight; + + // Calculate normals from wave function derivatives + vec3 waveNormal = vec3( + -waveHeight * 0.1 * cos(dot(pos, vec2(0.1, 0.17)) + time * waveSpeed), + 1.0, + -waveHeight * 0.1 * cos(dot(pos, vec2(0.17, 0.1)) + time * waveSpeed * 1.3) + ); + waveNormal = normalize(waveNormal); + + // Transform vertex + gl_Position = p3d_ModelViewProjectionMatrix * vertex; + + // Pass data to fragment shader + normal = normalize(p3d_NormalMatrix * waveNormal); + worldPos = (p3d_ModelMatrix * vertex).xyz; + texcoord = p3d_MultiTexCoord0; + + // Simple depth calculation + depth = gl_Position.z / gl_Position.w; +} \ No newline at end of file From 54f87a9230351acda7c7c4af4d92cb88f09fd9f4 Mon Sep 17 00:00:00 2001 From: Toontown Super <108632596+ToontownSuperForeverMVP@users.noreply.github.com> Date: Sun, 2 Aug 2026 02:56:19 -0400 Subject: [PATCH 2/2] Docs: Update GEMINI.md with completed porting items and PR #10 details --- GEMINI.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/GEMINI.md b/GEMINI.md index f6a02e8..6fe18d7 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -61,13 +61,14 @@ Target: `C:\Users\Shadow\Desktop\ttbtn` - [x] Implemented `ZonePrefetchCatalog.py`: Catalog mapping all hood zone IDs (TTC, DD, DG, MML, TB, DD, Speedway, Cog HQs, Estate) with async background preloading (`ZonePrefetchManager`). - [x] Integrated `ToontownLoader.py` & `ToontownLoadingScreen.py`: Automatic asset callback forwarding, heartbeat keepalive, and fallback support. - [x] Expanded `AssetCache.py`: Instant preloading of common Phase 3, 3.5, 4, 5 GUI models, chatboxes, shadows, and logos. -- [x] Submitted Pull Request #9 to GitHub repository (`feature/modern-loading-system`). +- [x] Ported updated `OutdoorLighting.py` (212 KB engine) and `ProceduralSky.py` into `toontown/hood/` with multi-threading guards and GSG depth-pass safety. +- [x] Ported GLSL shaders (`sky`, `sunrays`, `water`) into `toontown/shaders/` and added dynamic `_SHADER_DIR` resolution. +- [x] Ported `LegacyQuestDict.py` (15,29 procedural quest chains) and updated `toontown/quest/Quests.py` with `WANT_LEGACY_QUESTS` flag and `questExists()` helper. +- [x] Ported `QuestParser.py` into `toontown/quest/` and integrated `libotp` nametag/speech library dependencies. +- [x] Submitted Pull Request #9 (`feature/modern-loading-system`) and Pull Request #10 (`feature/outdoor-lighting-and-procedural-quests`) to GitHub repository. # TODO List for Future AI Instances -- [ ] Port updated `OutdoorLighting.py` (212 KB engine) and `ProceduralSky.py` into `toontown/hood/`. -- [ ] Port GLSL shaders (`sky`, `sunrays`, `water`) into `toontown/shaders/`. -- [ ] Port `LegacyQuestDict.py` and update `toontown/quest/Quests.py` with procedural quest chains. -- [ ] Port `QuestParser.py` into `toontown/quest/`. -- [ ] Verify `TaskAutoer.py` quest automation compatibility with the procedural quest dict. +- [ ] Merge and review PR #9 and PR #10 into `develop`. +- [ ] Verify `TaskAutoer.py` quest automation compatibility with the procedural quest dict during live server sessions. - [ ] Validate server stability with Astron local launcher under heavy zone switching.