Example #1
0
    def Configure(self) -> None:
        toggleEnabledButton = OptionBoxButton("Toggle Enabled")
        editTriggerButton = OptionBoxButton(
            "Edit Trigger",
            "Edits what redemption title triggers this effect. Not case sensitive, but must match exactly otherwise."
        )
        extraConfigurationButton = OptionBoxButton(
            "Extra Options", "Adjust extra, effect specific options.")

        def GetCaptionStr(trig: CrowdControl._Trigger) -> str:
            caption = ""
            if trig.IsEnabled:
                caption = f"Enabled: '{trig.Trigger}'"
            else:
                caption = "Disabled"
            caption += "\n" + trig.Effect.Description
            return caption

        class _EffectButton(OptionBoxButton):
            TrigObj: CrowdControl._Trigger

            def __init__(self, trig: CrowdControl._Trigger) -> None:
                self.TrigObj = trig

            @property
            def Name(self) -> str:  # type: ignore
                return f"{self.TrigObj.Effect.Name}   -   {GetCaptionStr(self.TrigObj)}"

        effectButtons = [_EffectButton(trig) for trig in self.Triggers]
        currentButton = effectButtons[0]

        selectBox = OptionBox(Title="Configure Effects",
                              Caption="Select the effect to configure.",
                              Buttons=effectButtons,
                              Tooltip=OptionBox.CreateTooltipString() +
                              "     " + "[R] Reset All")
        editBox = OptionBox(Title="Configure <effect>",
                            Caption="Enabled\n<description>",
                            Buttons=(toggleEnabledButton, editTriggerButton,
                                     extraConfigurationButton))
        renameBox = TextInputBox(Title="Configure <effect>")

        def UpdateEditBox() -> None:
            editBox.Title = f"Configure {currentButton.TrigObj.Effect.Name}"
            if currentButton.TrigObj.IsEnabled:
                editBox.Caption = f"Enabled: '{currentButton.TrigObj.Trigger}'"
            else:
                editBox.Caption = "Disabled"
            editBox.Caption += "\n" + currentButton.TrigObj.Effect.Description

            if currentButton.TrigObj.Effect.HasConfigMenu:
                editBox.Buttons = [
                    toggleEnabledButton, editTriggerButton,
                    extraConfigurationButton
                ]
            else:
                editBox.Buttons = [toggleEnabledButton, editTriggerButton]

            editBox.Update()

        def OnSelect(button: _EffectButton) -> None:
            nonlocal currentButton
            currentButton = button
            UpdateEditBox()
            editBox.Show()

        def OnEdit(button: OptionBoxButton) -> None:
            if button == toggleEnabledButton:
                currentButton.TrigObj.IsEnabled = not currentButton.TrigObj.IsEnabled
                for option in currentButton.TrigObj.Effect.Options:
                    option.IsHidden = not currentButton.TrigObj.IsEnabled
                UpdateEditBox()
                editBox.Show()
            elif button == editTriggerButton:
                renameBox.Title = editBox.Title
                renameBox.DefaultMessage = currentButton.TrigObj.Trigger
                renameBox.Show()
            elif button == extraConfigurationButton:
                currentButton.TrigObj.Effect.FinishConfiguration = editBox.Show  # type: ignore
                currentButton.TrigObj.Effect.ShowConfiguration()

        def OnRename(msg: str) -> None:
            if len(msg) > 0:
                currentButton.TrigObj.Trigger = msg
            UpdateEditBox()
            editBox.Show()

        oldSelectInput = selectBox.OnInput

        def OnSelectInput(key: str, event: int) -> None:
            if key == "R" and event == 1:
                for trig in self.Triggers:
                    trig.IsEnabled = True
                    trig.Trigger = trig.Effect.Name
                    for option in trig.Effect.Options:
                        option.IsHidden = False
                UpdateEditBox()
                selectBox.Hide()
                selectBox.Show()
            else:
                oldSelectInput(key, event)

        selectBox.OnPress = OnSelect  # type: ignore
        selectBox.OnCancel = self.SaveTriggers  # type: ignore
        selectBox.OnInput = OnSelectInput  # type:ignore
        editBox.OnPress = OnEdit  # type: ignore
        editBox.OnCancel = lambda: selectBox.ShowButton(currentButton
                                                        )  # type: ignore
        renameBox.OnSubmit = OnRename  # type: ignore
        selectBox.Show()
Example #2
0
class PresetManager:
    FileName: str
    PresetList: List[Preset]

    _NewPreset: ClassVar[OptionBoxButton] = OptionBoxButton(
        "Create New Preset")
    _OpenPresetFile: ClassVar[OptionBoxButton] = OptionBoxButton(
        "Open Preset File", "Useful for reordering or sharing presets.")
    _EditPreset: ClassVar[OptionBoxButton] = OptionBoxButton("Configure")
    _RenamePreset: ClassVar[OptionBoxButton] = OptionBoxButton("Rename")
    _DeletePreset: ClassVar[OptionBoxButton] = OptionBoxButton("Delete")
    _Yes: ClassVar[OptionBoxButton] = OptionBoxButton("Yes")
    _No: ClassVar[OptionBoxButton] = OptionBoxButton("No")

    _ConfigureBox: OptionBox
    _ButtonPresetMap: Dict[OptionBoxButton, Preset]

    _CurrentPreset: Optional[Preset]
    _PresetActionBox: OptionBox
    _ConfirmDeleteBox: OptionBox

    _RenameBox: TextInputBox

    def __init__(self, FileName: str):
        self.FileName = FileName

        self._ConfigureBox = OptionBox(
            Title="Configure Presets",
            Caption="Select the preset you want to configure",
            Tooltip=OptionBox.CreateTooltipString(EscMessage="Exit"),
            Buttons=(
                self._NewPreset,
                self._OpenPresetFile,
            ),
        )
        self._ConfigureBox.OnPress = self._SelectSpecificPreset  # type: ignore
        self.LoadPresets()
        self._UpdateConfigureBox()

        self._CurrentPreset = None

        # These two dialog boxes are mostly the constant, we'll just update their title as needed
        self._PresetActionBox = OptionBox(
            Title="Selected 'PRESET NAME'",
            Caption="Select the action to perform on this preset",
            Tooltip=OptionBox.CreateTooltipString(EscMessage="Back"),
            Buttons=(self._EditPreset, self._RenamePreset, self._DeletePreset),
        )
        self._PresetActionBox.OnPress = self._SelectPresetAction  # type: ignore
        self._PresetActionBox.OnCancel = self.StartConfiguring  # type: ignore

        self._ConfirmDeleteBox = OptionBox(
            Title="Delete 'PRESET NAME'",
            Caption="Are you sure you want to delete this preset?",
            Tooltip=OptionBox.CreateTooltipString(EscMessage="Back"),
            Buttons=(self._No, self._Yes),
        )
        self._ConfirmDeleteBox.OnPress = self._OnConfirmDelete  # type: ignore
        self._ConfirmDeleteBox.OnCancel = lambda: self._OnConfirmDelete(
            None)  # type: ignore

        self._RenameBox = TextInputBox("Rename 'PRESET NAME'")
        self._RenameBox.OnSubmit = self._OnPresetRename  # type: ignore

    def LoadPresets(self) -> None:
        loadedData: List[Dict[str, Union[str, Dict[str, str]]]] = []
        try:
            with open(self.FileName) as file:
                loadedData = json.load(file)
        except (FileNotFoundError, json.JSONDecodeError):
            pass

        self.PresetList = []
        for i in range(len(loadedData)):
            currentDict = loadedData[i]
            name: str = f"Preset {i}"
            settings: Dict[str, str] = {}

            if "Name" in currentDict:
                name = html.escape(str(currentDict["Name"]))

            if "Settings" in currentDict and isinstance(
                    currentDict["Settings"], dict):
                settings = currentDict["Settings"]
                # Sanity-check the data
                for cheat in _AllCheats:
                    if cheat.Name in settings:
                        value = settings[cheat.Name]
                        # We don't really care what this value is for regular cheats, but better to
                        #  keep it consistent
                        if not isinstance(cheat, ABCCycleableCheat):
                            value = "Run"
                        settings[cheat.Name] = value

            currentPreset = Preset(name, settings)
            currentPreset.OnFinishConfiguring = self._OnFinishConfiguringPreset  # type: ignore
            self.PresetList.append(currentPreset)

        # If there are no valid presets then still add the first one
        if len(self.PresetList) == 0:
            newPreset = Preset("Preset 1", {})
            newPreset.OnFinishConfiguring = self._OnFinishConfiguringPreset  # type: ignore
            self.PresetList.append(newPreset)

        # Save the sanity-checked info
        self.SavePresets()

    def SavePresets(self) -> None:
        data = []
        for preset in self.PresetList:
            data.append({
                "Name": html.unescape(preset.Name),
                "Settings": preset.GetSettings()
            })

        with open(self.FileName, "w") as file:
            json.dump(data, file, indent=2)

    def StartConfiguring(self) -> None:
        self.LoadPresets()
        self._UpdateConfigureBox()
        self._ConfigureBox.Show()

    # The next three functions should be overwritten by the main mod
    def AddKeybind(self, Name: str) -> None:
        raise NotImplementedError

    def RenameKeybind(self, OldName: str, NewName: str) -> None:
        raise NotImplementedError

    def RemoveKeybind(self, Name: str) -> None:
        raise NotImplementedError

    def _OnFinishConfiguringPreset(self) -> None:
        self.SavePresets()
        self.StartConfiguring()

    def _UpdateConfigureBox(self) -> None:
        buttonList = []
        self._ButtonPresetMap = {}
        for preset in self.PresetList:
            button = OptionBoxButton(html.unescape(preset.Name))
            buttonList.append(button)
            self._ButtonPresetMap[button] = preset

        buttonList += [
            self._NewPreset,
            self._OpenPresetFile,
        ]

        self._ConfigureBox.Buttons = buttonList
        self._ConfigureBox.Update()

    def _SelectSpecificPreset(self, button: OptionBoxButton) -> None:
        if button == self._NewPreset:
            # Get a new default name that's at least the size of the list + 1, or the largest
            #  existing default name + 1
            # This makes renaming or deleting cheats still add a somewhat logical name
            maxVal = len(self.PresetList)
            for preset in self.PresetList:
                val: int
                try:
                    val = int(preset.Name.split(" ")[-1])
                except ValueError:
                    continue
                if val > maxVal:
                    maxVal = val
            name = f"Preset {maxVal + 1}"

            newPreset = Preset(name, {})
            newPreset.OnFinishConfiguring = self._OnFinishConfiguringPreset  # type: ignore
            self.PresetList.append(newPreset)

            self.AddKeybind(name)

            self.SavePresets()
        elif button == self._OpenPresetFile:
            startfile(self.FileName)

        if button in self._ButtonPresetMap:
            self._CurrentPreset = self._ButtonPresetMap[button]
            self._PresetActionBox.Title = f"Selected '{self._CurrentPreset.Name}'"
            self._PresetActionBox.Update()
            self._PresetActionBox.Show()
            return

        self.StartConfiguring()

    def _SelectPresetAction(self, button: OptionBoxButton) -> None:
        if self._CurrentPreset is None:
            raise RuntimeError("Current Preset is None")
        if button == self._EditPreset:
            self._CurrentPreset.StartConfiguring()
            self._CurrentPreset = None
        elif button == self._RenamePreset:
            self._RenameBox.Title = f"Rename '{self._CurrentPreset.Name}'"
            self._RenameBox.DefaultMessage = self._CurrentPreset.Name
            self._RenameBox.Show()
        elif button == self._DeletePreset:
            self._ConfirmDeleteBox.Title = f"Delete '{self._CurrentPreset.Name}'"
            self._ConfirmDeleteBox.Update()
            self._ConfirmDeleteBox.Show()

    def _OnPresetRename(self, msg: str) -> None:
        if self._CurrentPreset is None:
            raise RuntimeError("Current Preset is None")

        if len(msg) > 0:
            self.RenameKeybind(self._CurrentPreset.Name, msg)

            self._CurrentPreset.UpdateName(msg)
            self.SavePresets()
            self._UpdateConfigureBox()

        self._PresetActionBox.Title = f"Selected '{self._CurrentPreset.Name}'"
        self._PresetActionBox.Update()
        self._PresetActionBox.ShowButton(self._RenamePreset)

    def _OnConfirmDelete(self, button: Optional[OptionBoxButton]) -> None:
        if self._CurrentPreset is None:
            raise RuntimeError("Current Preset is None")
        if button == self._Yes:
            self.PresetList.remove(self._CurrentPreset)
            self.RemoveKeybind(self._CurrentPreset.Name)
            self._CurrentPreset = None
            self.SavePresets()
            self.StartConfiguring()
        else:
            self._PresetActionBox.Show()