Settings & UI: Port persistent JSON Settings system and revamped tabbed OptionsPage ShtikerBook

This commit is contained in:
Toontown Super 2026-08-02 02:59:28 -04:00
parent 54f87a9230
commit ff1a95e360
12 changed files with 1381 additions and 722 deletions

View File

@ -4,3 +4,44 @@ from .SCStaticTextTerminal import SCStaticTextMsgEvent
from .SCGMTextTerminal import SCGMTextMsgEvent
from .SCCustomTerminal import SCCustomMsgEvent
from .SCEmoteTerminal import SCEmoteMsgEvent, SCEmoteNoAccessEvent
speedChatStyles = ((2000,
(200 / 255.0, 60 / 255.0, 229 / 255.0),
(200 / 255.0, 135 / 255.0, 255 / 255.0),
(220 / 255.0, 195 / 255.0, 229 / 255.0)),
(2001,
(0 / 255.0, 0 / 255.0, 255 / 255.0),
(140 / 255.0, 150 / 255.0, 235 / 255.0),
(201 / 255.0, 215 / 255.0, 255 / 255.0)),
(2002,
(90 / 255.0, 175 / 255.0, 225 / 255.0),
(120 / 255.0, 215 / 255.0, 255 / 255.0),
(208 / 255.0, 230 / 255.0, 250 / 255.0)),
(2003,
(130 / 255.0, 235 / 255.0, 235 / 255.0),
(120 / 255.0, 225 / 255.0, 225 / 255.0),
(234 / 255.0, 255 / 255.0, 255 / 255.0)),
(2004,
(0 / 255.0, 200 / 255.0, 70 / 255.0),
(0 / 255.0, 200 / 255.0, 80 / 255.0),
(204 / 255.0, 255 / 255.0, 204 / 255.0)),
(2005,
(235 / 255.0, 230 / 255.0, 0 / 255.0),
(255 / 255.0, 250 / 255.0, 100 / 255.0),
(255 / 255.0, 250 / 255.0, 204 / 255.0)),
(2006,
(255 / 255.0, 153 / 255.0, 0 / 255.0),
(229 / 255.0, 147 / 255.0, 0 / 255.0),
(255 / 255.0, 234 / 255.0, 204 / 255.0)),
(2007,
(255 / 255.0, 0 / 255.0, 50 / 255.0),
(229 / 255.0, 0 / 255.0, 50 / 255.0),
(255 / 255.0, 204 / 255.0, 204 / 255.0)),
(2008,
(255 / 255.0, 153 / 255.0, 193 / 255.0),
(240 / 255.0, 157 / 255.0, 192 / 255.0),
(255 / 255.0, 215 / 255.0, 238 / 255.0)),
(2009,
(170 / 255.0, 120 / 255.0, 20 / 255.0),
(165 / 255.0, 120 / 255.0, 50 / 255.0),
(210 / 255.0, 200 / 255.0, 180 / 255.0)))

View File

@ -1,4 +1,5 @@
from direct.distributed import DistributedObject
from direct.directnotify.DirectNotifyGlobal import directNotify
from toontown.effects.FireworkShowMixin import FireworkShowMixin
class DistributedFireworkShow(DistributedObject.DistributedObject, FireworkShowMixin):

View File

@ -16,6 +16,7 @@ from .PartyGlobals import FireworksFinishedEvent
from .PartyGlobals import FireworksPostLaunchDelay
from .PartyGlobals import RocketSoundDelay
from .PartyGlobals import RocketDirectionDelay
from direct.directnotify.DirectNotifyGlobal import directNotify
from .DistributedPartyActivity import DistributedPartyActivity
from .activityFSMs import FireworksActivityFSM
from . import PartyGlobals

View File

@ -11,6 +11,7 @@ from direct.gui.DirectGui import DirectFrame
from direct.gui.DirectGui import DirectButton
from direct.gui.DirectGui import DirectLabel
from direct.gui import DirectGuiGlobals
from direct.directnotify.DirectNotifyGlobal import directNotify
# Toontown imports
from toontown.toonbase import ToontownGlobals

View File

@ -0,0 +1,166 @@
from dataclasses import asdict, dataclass, fields
import json
from typing import Union, Any
from pathlib import Path
from direct.showbase.MessengerGlobal import messenger
# Valid types for a setting.
ControlSetting = dict[str, str]
Setting = Union[str, int, bool, list, float, ControlSetting]
@dataclass
class ControlSettings:
MOVE_UP: str = "arrow_up"
MOVE_DOWN: str = "arrow_down"
MOVE_LEFT: str = "arrow_left"
MOVE_RIGHT: str = "arrow_right"
JUMP: str = "control"
SPRINT: str = "shift"
SCREENSHOT: str = "f9"
TOGGLE_RUN_HOTKEY: str = "f8"
MAP_PAGE_HOTKEY: str = "escape"
FRIENDS_LIST_HOTKEY: str = "f7"
STREET_MAP_HOTKEY: str = "alt"
INVENTORY_HOTKEY: str = "home"
QUEST_HOTKEY: str = "end"
GALLERY_HOTKEY: str = "g"
LOCATIONS_HOTKEY: str = "v"
ELEVATOR_HOTKEY: str = "f"
CRANE_GRAB_KEY: str = "control"
ACTION_BUTTON: str = "delete"
SECONDARY_ACTION: str = "insert"
CHAT_HOTKEY: str = "t"
class Settings:
# All controls with their respective default values.
controls = ControlSettings()
# All settings with their respective default values.
defaultSettings = {
"borderless": False,
"music": True,
"sfx": True,
"toon-chat-sounds": True,
"resolution": [1280, 720],
"music-volume": 0.4,
"sfx-volume": 0.4,
"competitive-boss-scoring": True,
"report-errors": True,
"anti-aliasing": 0,
"anisotropic-filter": 8,
"frame-blending": True,
"controls": asdict(controls),
"vertical-sync": True,
"frame-rate-meter": False,
"fovEffects": True,
"cam-toggle-lock": False,
"movement_mode": "TTCC",
"sprint_mode": "Hold",
"magic-word-activator": 0,
"camSensitivityX": 0.25,
"camSensitivityY": 0.1,
"fps-limit": 0,
'laff-display': True,
'battle-speed': 2,
'random-music': False,
'boss-alerts': True,
# Lighting / shadows (OutdoorLighting.py)
'want-modern-outdoor-lighting': True,
'dynamic-shadows': True,
'shadow-quality': 'high', # off | low | medium | high
'lighting-bloom-enabled': True,
'lighting-god-rays': True,
'lighting-fog-enabled': True,
'lighting-specular-enabled': True,
'want-procedural-sky': True,
'want-day-night-cycle': False,
'want-water-reflections': True,
# Drop-shadow (non-shader) fallback controls
'drop-shadow-strength': 0.5,
# Debug
'lighting-debug': False,
# Options below this comment will not be exposed by OptionsPage
# They can still be configurable by the end user
"want-legacy-models": False,
"experimental-multithreading": False,
'discord-rich-presence': False,
"color-blind-mode": False,
}
settingsFile = Path.home() / "Documents" / "Super CFO" / "settings.json"
def __init__(self) -> None:
try:
with self.settingsFile.open(encoding='utf-8') as f:
self._settings = json.load(f)
except FileNotFoundError:
self.settingsFile.parent.mkdir(parents=True, exist_ok=True)
self._settings = self.defaultSettings.copy()
except Exception as e:
raise e
# Backfill any newly-added defaults into existing settings files.
# (clean() only removes unknown keys; it does not add missing ones.)
for k, v in self.defaultSettings.items():
if k not in self._settings:
self._settings[k] = v
# Re-instantiate the ControlSettings with our saved controls.
self.updateControls(self.get("controls"))
self.write()
def get(self, setting: str) -> Setting:
return self._settings.get(setting, self.defaultSettings.get(setting))
def getSetting(self, setting: str, default: Any = None) -> Any:
if setting == 'windowed-mode':
return not self.get('borderless')
val = self._settings.get(setting)
if val is None:
val = self.defaultSettings.get(setting, default)
return val
def readSettings(self) -> None:
pass
def set(self, setting: str, value: Setting) -> None:
if not isinstance(value, type(self.defaultSettings.get(setting))):
return
self._settings[setting] = value
def setControl(self, control: str, keybind: str) -> None:
# First, get the control setting.
controls: ControlSetting = self.get("controls")
# Replace the keybind at the control.
controls[control] = keybind
# Update the control settings with our new controls.
self.updateControls(controls)
def getControl(self, control: str) -> str:
return self.getControls().get(control, "")
def getControls(self) -> dict[str, Any]:
return asdict(self.controls)
def updateControls(self, controls: dict[str, str]) -> None:
# Ensure that extra controls in our list dont crash (typically for going back to a previous version)
valid_args = {f.name: controls[f.name] for f in fields(ControlSettings) if f.name in controls}
self.controls = ControlSettings(**valid_args)
self.set("controls", asdict(self.controls))
def write(self) -> None:
with self.settingsFile.open("w", encoding='utf-8') as _settings:
# Clean the settings dictionary before saving it to the file.
self.clean()
json.dump(self._settings, _settings, sort_keys=True, indent=4)
def clean(self) -> None:
"""
Removes all keys in settings which shouldn't exist.
(they don't exist in defaultSettings)
"""
for setting in list(self._settings):
if setting not in self.defaultSettings:
del self._settings[setting]

View File

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,118 @@
from direct.gui.DirectGui import DirectButton, DirectLabel
from panda3d.core import TextNode, Vec4
Preloaded = {}
def loadModels():
if Preloaded:
return
gui = loader.loadModel('phase_3.5/models/gui/fishingBook.bam')
Preloaded['tab1'] = gui.find('**/tabs/polySurface1')
Preloaded['tab2'] = gui.find('**/tabs/polySurface2')
gui.removeNode()
del gui
guiButton = loader.loadModel('phase_3/models/gui/quit_button')
Preloaded['button1'] = guiButton.find('**/QuitBtn_UP')
Preloaded['button2'] = guiButton.find('**/QuitBtn_DN')
Preloaded['button3'] = guiButton.find('**/QuitBtn_RLVR')
guiButton.removeNode()
del guiButton
normalColor = (1, 1, 1, 1)
clickColor = (0.8, 0.8, 0, 1)
rolloverColor = (0.15, 0.82, 1.0, 1)
diabledColor = (1.0, 0.98, 0.15, 1)
class OptionTab(DirectButton):
def __init__(self, tabType=2, parent=None, **kw):
loadModels()
if parent is None:
parent = aspect2d
if tabType == 1:
image = Preloaded['tab1']
elif tabType == 2:
image = Preloaded['tab2']
else:
image = None
optiondefs = (
('relief', None, None),
('text_align', TextNode.ALeft, None),
('text_fg', Vec4(0.2, 0.1, 0, 1), None),
('image', image, None),
('image_color', normalColor, None),
('image1_color', clickColor, None),
('image2_color', rolloverColor, None),
('image3_color', diabledColor, None),
('image_scale', (0.033, 0.033, 0.035), None),
('image_hpr', (0, 0, -90), None)
)
self.defineoptions(kw, optiondefs)
DirectButton.__init__(self, parent)
self.initialiseoptions(OptionTab)
buttonbase_xcoord = 0.35
buttonbase_ycoord = 0.45
class OptionButton(DirectButton):
def __init__(self, parent=None, wantLabel=False, z=buttonbase_ycoord, labelZ=None,
labelOrientation='left', labelPos=None, labelText='', image_scale=(0.7, 1, 1), text='', **kw):
loadModels()
if parent is None:
parent = aspect2d
pos = (buttonbase_xcoord, 0, z) if not kw.get('pos') else kw['pos']
optiondefs = (
('relief', None, None),
('image', (Preloaded['button1'], Preloaded['button2'], Preloaded['button3']), None),
('image_scale', image_scale, None),
('text', text, None),
('text_scale', 0.052, None),
('text_pos', (0, -0.02), None),
('pos', pos, None),
)
self.defineoptions(kw, optiondefs)
DirectButton.__init__(self, parent)
self.initialiseoptions(OptionButton)
if wantLabel:
self.label=OptionLabel(parent=self, z=labelZ, pos=labelPos, orientation=labelOrientation,
text=labelText)
titleHeight = 0.61
textStartHeight = 0.45
leftMargin = -0.72
class OptionLabel(DirectLabel):
def __init__(self, parent=None, z=textStartHeight, text_wordwrap=16, text='',
orientation='left', **kw):
loadModels()
if parent is None:
parent = aspect2d
if orientation == 'left':
pos = (leftMargin, 0, z)
text_align = TextNode.ALeft
else:
pos = kw['pos']
text_align = TextNode.ACenter
optiondefs = (
('relief', None, None),
('pos', pos, None),
('text_align', text_align, None),
('text_scale', 0.052, None),
('text_wordwrap', text_wordwrap, None),
('text', text, None)
)
self.defineoptions(kw, optiondefs)
DirectLabel.__init__(self, parent)
self.initialiseoptions(OptionLabel)

View File

@ -1,10 +1,9 @@
from panda3d.core import *
from panda3d.otp import *
from libotp import *
from toontown.toonbase import ToontownGlobals
from direct.showbase import DirectObject
from direct.fsm import StateData
from direct.gui.DirectGui import *
from panda3d.core import *
from toontown.toonbase import TTLocalizer
from toontown.effects import DistributedFireworkShow
from toontown.parties import DistributedPartyFireworksActivity
@ -32,11 +31,11 @@ class ShtikerBook(DirectFrame, StateData.StateData):
self.hide()
self.setPos(0, 0, 0.1)
self.pageOrder = [TTLocalizer.OptionsPageTitle,
TTLocalizer.ShardPageTitle,
TTLocalizer.CheckPageTitle,
TTLocalizer.LocationPageTitle,
TTLocalizer.MapPageTitle,
TTLocalizer.InventoryPageTitle,
TTLocalizer.QuestPageToonTasks,
TTLocalizer.TrackPageShortTitle,
TTLocalizer.SuitPageTitle,
TTLocalizer.FishPageTitle,
TTLocalizer.KartPageTitle,
@ -45,7 +44,8 @@ class ShtikerBook(DirectFrame, StateData.StateData):
TTLocalizer.GardenPageTitle,
TTLocalizer.GolfPageTitle,
TTLocalizer.EventsPageName,
TTLocalizer.NewsPageName]
TTLocalizer.NewsPageName,
TTLocalizer.SpellbookPageTitle]
return
def setSafeMode(self, setting):
@ -61,8 +61,8 @@ class ShtikerBook(DirectFrame, StateData.StateData):
messenger.send('stickerBookEntered')
base.playSfx(self.openSound)
base.disableMouse()
base.render.hide()
base.setBackgroundColor(0.05, 0.15, 0.4)
# base.render.hide()
# base.setBackgroundColor(0.05, 0.15, 0.4)
base.setCellsAvailable([base.rightCells[0]], 0)
self.oldMin2dAlpha = NametagGlobals.getMin2dAlpha()
self.oldMax2dAlpha = NametagGlobals.getMax2dAlpha()
@ -99,7 +99,7 @@ class ShtikerBook(DirectFrame, StateData.StateData):
else:
base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor)
gsg = base.win.getGsg()
if gsg:
if gsg and not base.config.GetString('threading-model', ''):
base.render.prepareScene(gsg)
NametagGlobals.setMin2dAlpha(self.oldMin2dAlpha)
NametagGlobals.setMax2dAlpha(self.oldMax2dAlpha)
@ -112,8 +112,8 @@ class ShtikerBook(DirectFrame, StateData.StateData):
self.ignore('shtiker-page-done')
self.ignore(ToontownGlobals.StickerBookHotkey)
self.ignore(ToontownGlobals.OptionsPageHotkey)
self.ignore('arrow_right')
self.ignore('arrow_left')
self.ignore(ToontownGlobals.StickerBookPageLeft)
self.ignore(ToontownGlobals.StickerBookPageRight)
if base.config.GetBool('want-qa-regression', 0):
self.notify.info('QA-REGRESSION: SHTICKERBOOK: Close')
@ -123,11 +123,8 @@ class ShtikerBook(DirectFrame, StateData.StateData):
self['image'] = bookModel.find('**/big_book')
self['image_scale'] = (2, 1, 1.5)
self.resetFrameSize()
# Widescreen support - maintain distance from right edge
baseXPos = 1.175
adjustedXPos = base.getWidescreenXOffset(baseXPos, 'right') if hasattr(base, 'getWidescreenXOffset') else baseXPos
self.bookOpenButton = DirectButton(image=(bookModel.find('**/BookIcon_CLSD'), bookModel.find('**/BookIcon_OPEN'), bookModel.find('**/BookIcon_RLVR')), relief=None, pos=(adjustedXPos, 0, -0.83), scale=0.305, command=self.__open)
self.bookCloseButton = DirectButton(image=(bookModel.find('**/BookIcon_OPEN'), bookModel.find('**/BookIcon_CLSD'), bookModel.find('**/BookIcon_RLVR2')), relief=None, pos=(adjustedXPos, 0, -0.83), scale=0.305, command=self.__close)
self.bookOpenButton = DirectButton(image=(bookModel.find('**/BookIcon_CLSD'), bookModel.find('**/BookIcon_OPEN'), bookModel.find('**/BookIcon_RLVR')), relief=None, pos=(-0.158, 0, 0.17), parent=base.a2dBottomRight, scale=0.305, command=self.__open)
self.bookCloseButton = DirectButton(image=(bookModel.find('**/BookIcon_OPEN'), bookModel.find('**/BookIcon_CLSD'), bookModel.find('**/BookIcon_RLVR2')), relief=None, pos=(-0.158, 0, 0.17), parent=base.a2dBottomRight, scale=0.305, command=self.__close)
self.bookOpenButton.hide()
self.bookCloseButton.hide()
self.nextArrow = DirectButton(parent=self, relief=None, image=(bookModel.find('**/arrow_button'), bookModel.find('**/arrow_down'), bookModel.find('**/arrow_rollover')), scale=(0.1, 0.1, 0.1), pos=(0.838, 0, -0.661), command=self.__pageChange, extraArgs=[1])
@ -210,12 +207,21 @@ class ShtikerBook(DirectFrame, StateData.StateData):
extraArgs = []
if pageName == TTLocalizer.OptionsPageTitle:
iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures')
iconGeom = iconModels.find('**/switch')
iconGeom = iconModels.find('**/switch1')
iconModels.detachNode()
elif pageName == TTLocalizer.ShardPageTitle:
iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures')
iconGeom = iconModels.find('**/district')
iconModels.detachNode()
elif pageName == TTLocalizer.CheckPageTitle:
iconModels = loader.loadModel('phase_4/models/parties/schtickerbookHostingGUI')
iconGeom = iconModels.find('**/checkmark')
iconScale = 25
iconModels.detachNode()
elif pageName == TTLocalizer.LocationPageTitle:
iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures')
iconGeom = iconModels.find('**/district')
iconModels.detachNode()
elif pageName == TTLocalizer.MapPageTitle:
iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures')
iconGeom = iconModels.find('**/teleportIcon')
@ -256,6 +262,11 @@ class ShtikerBook(DirectFrame, StateData.StateData):
iconModels = loader.loadModel('phase_3.5/models/gui/playingCard')
iconImage = iconModels.find('**/card_back')
iconGeom = iconModels.find('**/logo')
iconGeom.setState(RenderState.makeEmpty())
cardTex = loader.loadTexture('phase_3/maps/toontown-logo.png')
cardTex.setMinfilter(Texture.FTLinearMipmapLinear)
cardTex.setMagfilter(Texture.FTLinear)
iconGeom.setTexture(cardTex, 1)
iconScale = 0.22
iconModels.detachNode()
elif pageName == TTLocalizer.KartPageTitle:
@ -276,6 +287,10 @@ class ShtikerBook(DirectFrame, StateData.StateData):
iconModels.detachNode()
buttonPressedCommand = self.goToNewsPage
extraArgs = [page]
elif pageName == TTLocalizer.SpellbookPageTitle:
iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures')
iconGeom = iconModels.find('**/spellbookIcon')
iconModels.detachNode()
if pageName == TTLocalizer.OptionsPageTitle:
pageName = TTLocalizer.OptionsTabTitle
pageTab = DirectButton(parent=self.pageTabFrame, relief=DGG.RAISED, frameSize=(-0.575,
@ -415,16 +430,20 @@ class ShtikerBook(DirectFrame, StateData.StateData):
self.prevArrow.hide()
self.nextArrow.show()
def hidePageArrows(self):
self.prevArrow.hide()
self.nextArrow.hide()
def __checkForNewsPage(self):
from toontown.shtiker import NewsPage
self.ignore('arrow_left')
self.ignore('arrow_right')
self.ignore(ToontownGlobals.StickerBookPageLeft)
self.ignore(ToontownGlobals.StickerBookPageRight)
if isinstance(self.pages[self.currPageIndex], NewsPage.NewsPage):
self.ignore('arrow_left')
self.ignore('arrow_right')
self.ignore(ToontownGlobals.StickerBookPageLeft)
self.ignore(ToontownGlobals.StickerBookPageRight)
else:
self.accept('arrow_right', self.__pageChange, [1])
self.accept('arrow_left', self.__pageChange, [-1])
self.accept(ToontownGlobals.StickerBookPageRight, self.__pageChange, [1])
self.accept(ToontownGlobals.StickerBookPageLeft, self.__pageChange, [-1])
def goToNewsPage(self, page):
messenger.send('wakeup')

View File

@ -9791,3 +9791,111 @@ InteractivePropTrackBonusTerms = {0: 'Super Toon-Up!',
5: 'Super Squirt!',
6: ''}
PlayingCardUnknown = 'Card Name is unknown'
OptionsPageTabs = {
0: "Gameplay",
1: "Privacy",
2: "Controls",
3: "Video",
4: "Audio",
}
OptionNames = {
"camSensitivityX": "Camera X Sensitivity",
"camSensitivityY": "Camera Y Sensitivity",
"movement_mode": "Movement Mode",
"sprint_mode": "Sprint Mode",
"fovEffects": "Sprinting FOV Effects",
"cam-toggle-lock": "Camera Toggle Lock",
"speedchat-style": "Speedchat Color",
'discord-rich-presence': 'Discord Rich Presence',
'boss-alerts': "Cog Boss Attack Alerts",
"color-blind-mode": "Colorblind Mode",
"want-legacy-models": "Toggle TTO Species Models*",
'laff-display': "Toggle Overhead Laff Meter",
'battle-speed': "Battle Speed Multiplier",
"competitive-boss-scoring": "Want Competitive Boss Scoring",
"report-errors": "Report Crash Logs",
"JUMP": "Jump",
"MOVE_UP": "Move Up",
"MOVE_DOWN": "Move Down",
"MOVE_LEFT": "Move Left",
"MOVE_RIGHT": "Move Right",
"SPRINT": "Sprint",
"SCREENSHOT": "Screenshot",
"MAP_PAGE_HOTKEY": "Map Page Hotkey",
"FRIENDS_LIST_HOTKEY": "Friends List Hotkey",
"TOGGLE_RUN_HOTKEY": "Toggle Debug Run Speed",
"STREET_MAP_HOTKEY": "Street Map Hotkey",
"INVENTORY_HOTKEY": "Gag Menu Hotkey",
"QUEST_HOTKEY": "ToonTask Hotkey",
"GALLERY_HOTKEY": "Cog Gallery Hotkey",
"LOCATIONS_HOTKEY": "Location Tracker Hotkey",
"ELEVATOR_HOTKEY": "Skip Elevator Timer Hotkey",
"CRANE_GRAB_KEY": "Crane Grab Key",
"ACTION_BUTTON": "Action Button",
"SECONDARY_ACTION": "Secondary Action Button",
"CHAT_HOTKEY": "Chat Button",
"borderless": "Borderless Window",
"resolution": "Window Resolution",
"vertical-sync": "Vertical Sync",
"anisotropic-filter": "Anisotropic Filtering",
"anti-aliasing": "Anti-Aliasing*",
"frame-rate-meter": "FPS Meter",
"fps-limit": "FPS Limit",
"want-modern-outdoor-lighting": "Modern Outdoor Lighting",
"dynamic-shadows": "Dynamic Shadows",
"shadow-quality": "Shadow Quality",
"lighting-bloom-enabled": "Bloom",
"lighting-god-rays": "God Rays",
"want-procedural-sky": "Procedural Sky",
"want-day-night-cycle": "Day/Night Cycle",
"want-water-reflections": "Water Reflections",
"drop-shadow-strength": "Drop Shadow Strength",
"music": "Music",
"sfx": "Sound Effects",
"music-volume": "Music Volume",
"sfx-volume": "Sound Effects Volume",
"toon-chat-sounds": "Toon Chat Sounds",
"random-music": "Randomize Music",
'ap-sounds': "AP Item Sounds",
'refresh-audio': "Refresh Audio",
}
OptionAntiAlias = {
0: "Off",
4: "4x MSAA",
8: "8x MSAA",
}
OptionBattleSpeed = {
1: "1x Speed",
2: "2x Speed",
3: "3x Speed",
4: "4x Speed",
5: "5x Speed"
}
OptionAnisotropic = {
0: "Off",
2: "Lowest",
4: "Low",
8: "High",
16: "Highest",
}
OptionFPSLimit = {
0: "Unlimited",
30: "30 FPS",
60: "60 FPS",
120: "120 FPS",
144: "144 FPS",
165: "165 FPS",
240: "240 FPS",
360: "360 FPS"
}
OptionEnabled = "Enabled"
OptionDisabled = "Disabled"
OptionRefresh = "Refresh"
WinConditionMet = 'Congratulations, you have completed your goal! You may now talk to Flippy to redeem your reward!'
RefreshDialogMsg = "Refreshing audio devices..."

View File

@ -1,7 +1,7 @@
from otp.otpbase import OTPBase
from otp.otpbase import OTPLauncherGlobals
from otp.otpbase import OTPGlobals
from otp.settings.Settings import Settings
from toontown.settings.Settings import Settings
from direct.showbase.PythonUtil import *
from . import ToontownGlobals
from direct.directnotify import DirectNotifyGlobal

View File

@ -0,0 +1,98 @@
from direct.gui.DirectGui import DirectFrame, DGG, DirectScrollBar
from panda3d.core import NodePath, PGScrollFrame
class ToontownScrolledFrame(DirectFrame):
def __init__(self, parent=None, **kw):
# Merge keyword options with default options
self.defineoptions(kw, (
('scrollDistance', 0.2, None),
('scrollCondition', True, None),
('pgFunc', PGScrollFrame, None),
('frameSize', (-0.5, 0.5, -0.5, 0.5), None),
('canvasSize', (-1, 1, -1, 1), self.setCanvasSize),
('manageScrollBars', 1, self.setManageScrollBars),
('autoHideScrollBars', 1, self.setAutoHideScrollBars),
('scrollBarWidth', 0.08, self.setScrollBarWidth),
('borderWidth', (0.01, 0.01), self.setBorderWidth),
))
super().__init__(parent, **kw)
w = self['scrollBarWidth']
self.verticalScroll = self.createcomponent(
"verticalScroll", (), None,
DirectScrollBar, (self,),
borderWidth=self['borderWidth'],
frameSize=(-w / 2.0, w / 2.0, -1, 1),
orientation=DGG.VERTICAL
)
self.guiItem.setVerticalSlider(self.verticalScroll.guiItem)
self.canvas = NodePath(self.guiItem.getCanvasNode())
# Call option initialization functions
self.initialiseoptions(ToontownScrolledFrame)
# Bind the scroll wheel.
self['state'] = DGG.NORMAL
self.bindToScroll(self)
self.bindToScroll(self.verticalScroll)
self.bindToScroll(self.verticalScroll.thumb)
self.bindToScroll(self.verticalScroll.incButton)
self.bindToScroll(self.verticalScroll.decButton)
def scroll(self, direction):
if bool(self['scrollCondition']) or (callable(self['scrollCondition']) and bool(self['scrollCondition']())):
scrollPercent = self['scrollDistance'] / abs(self['canvasSize'][2] - self['canvasSize'][3])
self['verticalScroll_value'] = self['verticalScroll_value'] + scrollPercent * direction
def bindToScroll(self, gui):
gui.bind(DGG.WHEELUP, lambda _: self.scroll(-1))
gui.bind(DGG.WHEELDOWN, lambda _: self.scroll(1))
def setScrollBarWidth(self):
if self.fInit:
return
w = self['scrollBarWidth']
self.verticalScroll["frameSize"] = (
-w / 2.0, w / 2.0, self.verticalScroll["frameSize"][2], self.verticalScroll["frameSize"][3])
def setCanvasSize(self):
f = self['canvasSize']
self.guiItem.setVirtualFrame(f[0], f[1], f[2], f[3])
def getCanvas(self):
return self.canvas
def setManageScrollBars(self):
self.guiItem.setManagePieces(self['manageScrollBars'])
def setAutoHideScrollBars(self):
self.guiItem.setAutoHide(self['autoHideScrollBars'])
def commandFunc(self):
if self['command']:
self['command'](*self['extraArgs'])
def destroy(self):
# Destroy children of the canvas
for child in self.canvas.getChildren():
childGui = self.guiDict.get(child.getName())
if childGui:
childGui.destroy()
else:
parts = child.getName().split('-')
simpleChildGui = self.guiDict.get(parts[-1])
if simpleChildGui:
simpleChildGui.destroy()
if self.verticalScroll:
self.verticalScroll.destroy()
self.verticalScroll = None
super().destroy()