diff --git a/korman/__init__.py b/korman/__init__.py index f7b0df8..824cfd8 100644 --- a/korman/__init__.py +++ b/korman/__init__.py @@ -14,6 +14,7 @@ # along with Korman. If not, see . import bpy +from . import addon_prefs from . import exporter, render from . import properties, ui from . import nodes @@ -26,8 +27,7 @@ bl_info = { "location": "File > Import-Export", "description": "Exporter for Cyan Worlds' Plasma Engine", "warning": "beta", - "category": "System", # Eventually, we will hide some of the default - # Blender panels (think materials) + "category": "System", } diff --git a/korman/addon_prefs.py b/korman/addon_prefs.py new file mode 100644 index 0000000..94a3011 --- /dev/null +++ b/korman/addon_prefs.py @@ -0,0 +1,76 @@ +# This file is part of Korman. +# +# Korman is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Korman is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Korman. If not, see . + +import bpy +from bpy.props import * + +game_versions = [("pvPrime", "Ages Beyond Myst (63.11)", "Targets the original Uru (Live) game"), + ("pvPots", "Path of the Shell (63.12)", "Targets the most recent offline expansion pack"), + ("pvMoul", "Myst Online: Uru Live (70)", "Targets the most recent online game")] + +class PlasmaGame(bpy.types.PropertyGroup): + name = StringProperty(name="Name", + description="Name of the Plasma Game", + options=set()) + path = StringProperty(name="Path", + description="Path to this Plasma Game", + options=set()) + version = EnumProperty(name="Version", + description="Plasma version of this game", + items=game_versions, + options=set()) + + +class KormanAddonPreferences(bpy.types.AddonPreferences): + bl_idname = __package__ + + games = CollectionProperty(type=PlasmaGame) + active_game_index = IntProperty(options={"SKIP_SAVE"}) + + def draw(self, context): + layout = self.layout + + layout.label("Plasma Games:") + row = layout.row() + row.template_list("PlasmaGameListRW", "games", self, "games", self, + "active_game_index", rows=2) + col = row.column(align=True) + col.operator("world.plasma_game_add", icon="ZOOMIN", text="") + col.operator("world.plasma_game_remove", icon="ZOOMOUT", text="") + col.operator("world.plasma_game_convert", icon="IMPORT", text="") + + # Game Properties + active_game_index = self.active_game_index + if bool(self.games) and active_game_index < len(self.games): + active_game = self.games[active_game_index] + + layout.separator() + box = layout.box() + + box.prop(active_game, "path", emboss=False) + box.prop(active_game, "version") + box.separator() + + row = box.row(align=True) + op = row.operator("world.plasma_game_add", icon="FILE_FOLDER", text="Change Path") + op.filepath = active_game.path + op.game_index = active_game_index + + @classmethod + def register(cls): + # Register the old-timey per-world Plasma Games for use in the conversion + # operator. What fun. I guess.... + from .properties.prop_world import PlasmaGames + PlasmaGames.games = CollectionProperty(type=PlasmaGame) diff --git a/korman/operators/op_export.py b/korman/operators/op_export.py index 3a098f9..adec5bd 100644 --- a/korman/operators/op_export.py +++ b/korman/operators/op_export.py @@ -19,9 +19,10 @@ import cProfile from pathlib import Path import pstats +from ..addon_prefs import game_versions from .. import exporter from ..helpers import UiHelper -from ..properties.prop_world import PlasmaAge, game_versions +from ..properties.prop_world import PlasmaAge from ..korlib import ConsoleToggler class ExportOperator(bpy.types.Operator): diff --git a/korman/operators/op_ui.py b/korman/operators/op_ui.py index 0f23da4..ffa3b17 100644 --- a/korman/operators/op_ui.py +++ b/korman/operators/op_ui.py @@ -13,6 +13,7 @@ # You should have received a copy of the GNU General Public License # along with Korman. If not, see . +import addon_utils import bpy from bpy.props import * @@ -87,3 +88,17 @@ class CollectionRemoveOperator(UIOperator, bpy.types.Operator): return {"FINISHED"} else: return {"CANCELLED"} + + +class OpenAddonPrefs(UIOperator, bpy.types.Operator): + bl_idname = "ui.korman_open_prefs" + bl_label = "Open Korman Preferences" + bl_description = "Opens the Korman User Preferences" + + def execute(self, context): + bpy.ops.screen.userpref_show("INVOKE_DEFAULT") + context.user_preferences.active_section = "ADDONS" + context.window_manager.addon_filter = "System" + korman_addon = addon_utils.addons_fake_modules["korman"] + addon_utils.module_bl_info(korman_addon)["show_expanded"] = True + return {"FINISHED"} diff --git a/korman/operators/op_world.py b/korman/operators/op_world.py index 7affe66..866a580 100644 --- a/korman/operators/op_world.py +++ b/korman/operators/op_world.py @@ -32,38 +32,35 @@ class GameAddOperator(AgeOperator, bpy.types.Operator): game_index = IntProperty(default=-1, options={"HIDDEN"}) def execute(self, context): - w = context.world - if w: - # First, verify this is a valid Uru directory... - path = Path(self.filepath) - - # Blendsucks likes to tack filenames onto our doggone directories... - if not path.is_dir(): - path = path.parent - if not ((path / "UruExplorer.exe").is_file() or (path / "plClient.exe").is_file()): - self.report({"ERROR"}, "The selected directory is not a copy of URU.") - return {"CANCELLED"} + prefs = context.user_preferences.addons["korman"].preferences - # New game? - games = w.plasma_games - new_game = self.game_index == -1 - if new_game: - games.active_game_index = len(games.games) - game = games.games.add() - else: - game = games.games[self.game_index] + # First, verify this is a valid Uru directory... + path = Path(self.filepath) - # Setup game... - game.path = str(path) - if (path / "cypython22.dll").is_file(): - game.version = "pvPots" - else: - game.version = "pvMoul" - game.name = path.name + # Blendsucks likes to tack filenames onto our doggone directories... + if not path.is_dir(): + path = path.parent + if not ((path / "UruExplorer.exe").is_file() or (path / "plClient.exe").is_file()): + self.report({"ERROR"}, "The selected directory is not a copy of URU.") + return {"CANCELLED"} - return {"FINISHED"} + # New game? + new_game = self.game_index == -1 + if new_game: + prefs.active_game_index = len(prefs.games) + game = prefs.games.add() else: - return {"CANCELLED"} + game = prefs.games[self.game_index] + + # Setup game... + game.path = str(path) + if (path / "cypython22.dll").is_file(): + game.version = "pvPots" + else: + game.version = "pvMoul" + game.name = path.name + + return {"FINISHED"} def invoke(self, context, event): @@ -71,20 +68,53 @@ class GameAddOperator(AgeOperator, bpy.types.Operator): return {"RUNNING_MODAL"} +class GameConvertOperator(AgeOperator, bpy.types.Operator): + bl_idname = "world.plasma_game_convert" + bl_label = "This will save your User Preferences file!" + bl_description = "Load old per-file Plasma Games into your user preferences" + + def draw(self, context): + self.layout.label("test") + + def execute(self, context): + prefs = context.user_preferences.addons["korman"].preferences + w = context.scene.world + + for old_game in w.plasma_games.games: + # don't add dupe games + match = next((i for i in prefs.games if i.path == old_game.path), None) + if match is not None: + continue + + new_game = prefs.games.add() + new_game.name = old_game.name + new_game.path = old_game.path + new_game.version = old_game.version + + w.plasma_games.games.clear() + bpy.ops.wm.save_userpref() + return {"FINISHED"} + + def invoke(self, context, event): + return context.window_manager.invoke_confirm(self, event) + + @classmethod + def poll(cls, context): + return super().poll(context) and bool(context.scene.world.plasma_games.games) + + class GameRemoveOperator(AgeOperator, bpy.types.Operator): bl_idname = "world.plasma_game_remove" bl_label = "Remove Plasma Game" def execute(self, context): - w = context.world - if w: - games = w.plasma_games - if games.active_game_index >= len(games.games): - return {"CANCELLED"} - games.games.remove(games.active_game_index) - return {"FINISHED"} - else: + prefs = context.user_preferences.addons["korman"].preferences + + if prefs.active_game_index >= len(prefs.games): return {"CANCELLED"} + prefs.games.remove(prefs.active_game_index) + prefs.active_game_index = max(prefs.active_game_index - 1, -1) + return {"FINISHED"} class PageAddOperator(AgeOperator, bpy.types.Operator): diff --git a/korman/properties/modifiers/gui.py b/korman/properties/modifiers/gui.py index 257c3bb..62cecdf 100644 --- a/korman/properties/modifiers/gui.py +++ b/korman/properties/modifiers/gui.py @@ -20,8 +20,8 @@ import mathutils from bpy.props import * from PyHSPlasma import * +from ...addon_prefs import game_versions from .base import PlasmaModifierProperties, PlasmaModifierLogicWiz -from .logic import game_versions from ... import idprops diff --git a/korman/properties/modifiers/logic.py b/korman/properties/modifiers/logic.py index 2dea10e..261bc02 100644 --- a/korman/properties/modifiers/logic.py +++ b/korman/properties/modifiers/logic.py @@ -17,8 +17,8 @@ import bpy from bpy.props import * from PyHSPlasma import * +from ...addon_prefs import game_versions from .base import PlasmaModifierProperties -from ..prop_world import game_versions from ...exporter import ExportError from ... import idprops diff --git a/korman/properties/prop_world.py b/korman/properties/prop_world.py index 9f32b43..1e159a5 100644 --- a/korman/properties/prop_world.py +++ b/korman/properties/prop_world.py @@ -17,9 +17,7 @@ import bpy from bpy.props import * from PyHSPlasma import * -game_versions = [("pvPrime", "Ages Beyond Myst (63.11)", "Targets the original Uru (Live) game"), - ("pvPots", "Path of the Shell (63.12)", "Targets the most recent offline expansion pack"), - ("pvMoul", "Myst Online: Uru Live (70)", "Targets the most recent online game")] +from ..addon_prefs import game_versions class PlasmaFni(bpy.types.PropertyGroup): bl_idname = "world.plasma_fni" @@ -58,23 +56,9 @@ class PlasmaFni(bpy.types.PropertyGroup): min=1) -class PlasmaGame(bpy.types.PropertyGroup): - name = StringProperty(name="Name", - description="Name of the Plasma Game", - options=set()) - path = StringProperty(name="Path", - description="Path to this Plasma Game", - options=set()) - version = EnumProperty(name="Version", - description="Plasma version of this game", - items=game_versions, - options=set()) - - class PlasmaGames(bpy.types.PropertyGroup): bl_idname = "world.plasma_games" - games = CollectionProperty(type=PlasmaGame) active_game_index = IntProperty(options={"HIDDEN"}) @property diff --git a/korman/ui/ui_world.py b/korman/ui/ui_world.py index c5580c4..543cc26 100644 --- a/korman/ui/ui_world.py +++ b/korman/ui/ui_world.py @@ -34,41 +34,39 @@ class PlasmaGamePanel(AgeButtonsPanel, bpy.types.Panel): def draw(self, context): layout = self.layout + prefs = context.user_preferences.addons["korman"].preferences games = context.world.plasma_games age = context.world.plasma_age row = layout.row() - row.template_list("PlasmaGameList", "games", games, "games", games, + # Remember: game storage moved to addon preferences! + row.template_list("PlasmaGameListRO", "games", prefs, "games", games, "active_game_index", rows=2) - col = row.column(align=True) - col.operator("world.plasma_game_add", icon="ZOOMIN", text="") - col.operator("world.plasma_game_remove", icon="ZOOMOUT", text="") + row.operator("ui.korman_open_prefs", icon="PREFERENCES", text="") - # Game Properties + # Game Tools active_game_index = games.active_game_index - if active_game_index < len(games.games): - active_game = games.games[active_game_index] + if active_game_index < len(prefs.games): + active_game = prefs.games[active_game_index] + else: + active_game = None - layout.separator() - box = layout.box() + layout.separator() + row = layout.row(align=True) - box.prop(active_game, "path", emboss=False) - box.prop(active_game, "version") - box.separator() - - row = box.row(align=True) - op = row.operator("world.plasma_game_add", icon="FILE_FOLDER", text="Change Path") - op.filepath = active_game.path - op.game_index = active_game_index - row = row.row(align=True) - row.operator_context = "EXEC_DEFAULT" - row.enabled = bool(age.age_name.strip()) - op = row.operator("export.plasma_age", icon="EXPORT") + row.operator_context = "EXEC_DEFAULT" + row.enabled = bool(age.age_name.strip()) and active_game is not None + op = row.operator("export.plasma_age", icon="EXPORT") + if active_game is not None: op.filepath = str((Path(active_game.path) / "dat" / age.age_name).with_suffix(".age")) op.version = active_game.version -class PlasmaGameList(bpy.types.UIList): +class PlasmaGameListRO(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0): + layout.label(item.name, icon="BOOKMARKS") + +class PlasmaGameListRW(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0): layout.prop(item, "name", text="", emboss=False, icon="BOOKMARKS")