From 3e7b22c3443fd4b4468e7f1b384a30d8076ad5f5 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 26 May 2018 13:45:19 -0400 Subject: [PATCH 1/4] Rudimentary game tracking --- korman/operators/op_export.py | 3 +- korman/operators/op_world.py | 66 +++++++++++++++++++++++++++- korman/properties/__init__.py | 1 + korman/properties/modifiers/logic.py | 5 +-- korman/properties/prop_world.py | 33 ++++++++++++++ korman/ui/ui_world.py | 37 ++++++++++++++++ 6 files changed, 138 insertions(+), 7 deletions(-) diff --git a/korman/operators/op_export.py b/korman/operators/op_export.py index d65ee5c..1b7c4b7 100644 --- a/korman/operators/op_export.py +++ b/korman/operators/op_export.py @@ -20,8 +20,7 @@ from pathlib import Path import pstats from .. import exporter -from ..properties.prop_world import PlasmaAge -from ..properties.modifiers.logic import game_versions +from ..properties.prop_world import PlasmaAge, game_versions from ..korlib import ConsoleToggler class ExportOperator(bpy.types.Operator): diff --git a/korman/operators/op_world.py b/korman/operators/op_world.py index 93b79d2..86e9f39 100644 --- a/korman/operators/op_world.py +++ b/korman/operators/op_world.py @@ -14,7 +14,8 @@ # along with Korman. If not, see . import bpy - +from bpy.props import * +from pathlib import Path class AgeOperator: @classmethod @@ -22,6 +23,69 @@ class AgeOperator: return context.scene.render.engine == "PLASMA_GAME" +class GameAddOperator(AgeOperator, bpy.types.Operator): + bl_idname = "world.plasma_game_add" + bl_label = "Add Plasma Game" + + filepath = StringProperty(subtype="DIR_PATH") + directory = BoolProperty(default=True, options={"HIDDEN"}) + 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) + if not path.is_dir(): + self.report({"ERROR"}, "The selection is not a valid directory.") + return {"CANCELLED"} + 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"} + + # 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] + + # Setup game... + game.path = self.filepath + if (path / "cypython22.dll").is_file(): + game.version = "pvPots" + else: + game.version = "pvMoul" + game.name = path.name + + return {"FINISHED"} + else: + return {"CANCELLED"} + + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {"RUNNING_MODAL"} + + +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: + return {"CANCELLED"} + + class PageAddOperator(AgeOperator, bpy.types.Operator): bl_idname = "world.plasma_page_add" bl_label = "Add Page" diff --git a/korman/properties/__init__.py b/korman/properties/__init__.py index b24bb8e..dc98a70 100644 --- a/korman/properties/__init__.py +++ b/korman/properties/__init__.py @@ -29,3 +29,4 @@ def register(): bpy.types.Texture.plasma_layer = bpy.props.PointerProperty(type=PlasmaLayer) bpy.types.World.plasma_age = bpy.props.PointerProperty(type=PlasmaAge) bpy.types.World.plasma_fni = bpy.props.PointerProperty(type=PlasmaFni) + bpy.types.World.plasma_games = bpy.props.PointerProperty(type=PlasmaGames) diff --git a/korman/properties/modifiers/logic.py b/korman/properties/modifiers/logic.py index 339fd9e..2dea10e 100644 --- a/korman/properties/modifiers/logic.py +++ b/korman/properties/modifiers/logic.py @@ -18,13 +18,10 @@ from bpy.props import * from PyHSPlasma import * from .base import PlasmaModifierProperties +from ..prop_world import game_versions from ...exporter import ExportError from ... import idprops -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 PlasmaVersionedNodeTree(idprops.IDPropMixin, bpy.types.PropertyGroup): name = StringProperty(name="Name") version = EnumProperty(name="Version", diff --git a/korman/properties/prop_world.py b/korman/properties/prop_world.py index badba65..557a19d 100644 --- a/korman/properties/prop_world.py +++ b/korman/properties/prop_world.py @@ -17,6 +17,9 @@ 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")] class PlasmaFni(bpy.types.PropertyGroup): bl_idname = "world.plasma_fni" @@ -55,6 +58,36 @@ 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 + def active_game(self): + if len(self.games) > self.active_game_index and self.active_game_index != -1: + return self.games[self.active_game_index] + return None + + @property + def is_game_active(self): + return len(self.games) > 0 and self.active_game_index != -1 + + class PlasmaPage(bpy.types.PropertyGroup): def _check_suffix(self, context): """Verifies that a suffix change does not conflict""" diff --git a/korman/ui/ui_world.py b/korman/ui/ui_world.py index 3b51392..58324d4 100644 --- a/korman/ui/ui_world.py +++ b/korman/ui/ui_world.py @@ -26,6 +26,43 @@ class AgeButtonsPanel: return context.world and context.scene.render.engine == "PLASMA_GAME" +class PlasmaGamePanel(AgeButtonsPanel, bpy.types.Panel): + bl_label = "Plasma Games" + + def draw(self, context): + layout = self.layout + games = context.world.plasma_games + + row = layout.row() + row.template_list("PlasmaGameList", "games", games, "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="") + + # Game Properties + active_game_index = games.active_game_index + if active_game_index < len(games.games): + active_game = games.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 + + +class PlasmaGameList(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") + + class PlasmaPageList(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") From bbb02094a174f1675917a645c4ab95094e8fb54f Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 26 May 2018 14:47:05 -0400 Subject: [PATCH 2/4] Add the ability to export to tracked games --- korman/operators/op_export.py | 5 ++--- korman/properties/prop_world.py | 4 ++++ korman/ui/ui_world.py | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/korman/operators/op_export.py b/korman/operators/op_export.py index 1b7c4b7..49029c3 100644 --- a/korman/operators/op_export.py +++ b/korman/operators/op_export.py @@ -83,8 +83,7 @@ class ExportOperator(bpy.types.Operator): @classmethod def poll(cls, context): - if context.object is not None: - return context.scene.render.engine == "PLASMA_GAME" + return context.scene.render.engine == "PLASMA_GAME" def execute(self, context): # Before we begin, do some basic sanity checking... @@ -132,7 +131,7 @@ class ExportOperator(bpy.types.Operator): if not self.filepath: blend_filepath = context.blend_data.filepath if not blend_filepath: - blend_filepath = "Korman" + blend_filepath = context.scene.world.plasma_age.age_name self.filepath = str(Path(blend_filepath).with_suffix(".age")) context.window_manager.fileselect_add(self) return {"RUNNING_MODAL"} diff --git a/korman/properties/prop_world.py b/korman/properties/prop_world.py index 557a19d..fdcfb95 100644 --- a/korman/properties/prop_world.py +++ b/korman/properties/prop_world.py @@ -191,6 +191,10 @@ class PlasmaAge(bpy.types.PropertyGroup): use_texture_page = BoolProperty(name="Use Textures Page", description="Exports all textures to a dedicated Textures page", default=True) + age_name = StringProperty(name="Age Name", + description="Name of the Age to be used for data files", + default="Korman Age", + options=set()) # Implementation details active_page_index = IntProperty(name="Active Page Index") diff --git a/korman/ui/ui_world.py b/korman/ui/ui_world.py index 58324d4..695449c 100644 --- a/korman/ui/ui_world.py +++ b/korman/ui/ui_world.py @@ -14,6 +14,9 @@ # along with Korman. If not, see . import bpy +from pathlib import Path + +from ..korlib import ConsoleToggler class AgeButtonsPanel: @@ -32,6 +35,7 @@ class PlasmaGamePanel(AgeButtonsPanel, bpy.types.Panel): def draw(self, context): layout = self.layout games = context.world.plasma_games + age = context.world.plasma_age row = layout.row() row.template_list("PlasmaGameList", "games", games, "games", games, @@ -56,6 +60,9 @@ class PlasmaGamePanel(AgeButtonsPanel, bpy.types.Panel): 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.operator_context = "EXEC_DEFAULT" + op = row.operator("export.plasma_age", icon="EXPORT") + op.filepath = str((Path(active_game.path) / "dat" / age.age_name).with_suffix(".age")) class PlasmaGameList(bpy.types.UIList): @@ -114,10 +121,26 @@ class PlasmaAgePanel(AgeButtonsPanel, bpy.types.Panel): col = split.column() col.label("Age Settings:") col.prop(age, "seq_prefix", text="ID") + col.prop(age, "age_name", text="") + + layout.separator() + split = layout.split() + + col = split.column() + col.label("Export Settings:") + col.prop(age, "bake_lighting") + cons_ui = col.column() + cons_ui.enabled = ConsoleToggler.is_platform_supported() + cons_ui.prop(age, "verbose") + cons_ui.prop(age, "show_console") + + col = split.column() + col.label("Plasma Settings:") col.prop(age, "age_sdl") col.prop(age, "use_texture_page") + class PlasmaEnvironmentPanel(AgeButtonsPanel, bpy.types.Panel): bl_label = "Plasma Environment" From b5cf75afd1b1d52cda6a3595fc8a3566aa5432bf Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sun, 17 Jun 2018 20:52:11 -0400 Subject: [PATCH 3/4] Handle filepaths better This change lops off any filename portion of a path and attempts to verify that it is a valid Uru directory. --- korman/operators/op_world.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/korman/operators/op_world.py b/korman/operators/op_world.py index 86e9f39..7affe66 100644 --- a/korman/operators/op_world.py +++ b/korman/operators/op_world.py @@ -36,9 +36,10 @@ class GameAddOperator(AgeOperator, bpy.types.Operator): 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(): - self.report({"ERROR"}, "The selection is not a valid directory.") - return {"CANCELLED"} + 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"} @@ -53,7 +54,7 @@ class GameAddOperator(AgeOperator, bpy.types.Operator): game = games.games[self.game_index] # Setup game... - game.path = self.filepath + game.path = str(path) if (path / "cypython22.dll").is_file(): game.version = "pvPots" else: From 8b9846d712846b5e350ee80ba58dfea6e6074057 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sun, 17 Jun 2018 21:02:42 -0400 Subject: [PATCH 4/4] Fix some usability issues with age names. Disallow exporting from the World panel if the age name is blank and draw attention to it in the UI. --- korman/properties/prop_world.py | 1 - korman/ui/ui_world.py | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/korman/properties/prop_world.py b/korman/properties/prop_world.py index fdcfb95..5e4308d 100644 --- a/korman/properties/prop_world.py +++ b/korman/properties/prop_world.py @@ -193,7 +193,6 @@ class PlasmaAge(bpy.types.PropertyGroup): default=True) age_name = StringProperty(name="Age Name", description="Name of the Age to be used for data files", - default="Korman Age", options=set()) # Implementation details diff --git a/korman/ui/ui_world.py b/korman/ui/ui_world.py index 695449c..6452855 100644 --- a/korman/ui/ui_world.py +++ b/korman/ui/ui_world.py @@ -60,7 +60,9 @@ class PlasmaGamePanel(AgeButtonsPanel, bpy.types.Panel): 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") op.filepath = str((Path(active_game.path) / "dat" / age.age_name).with_suffix(".age")) @@ -121,6 +123,7 @@ class PlasmaAgePanel(AgeButtonsPanel, bpy.types.Panel): col = split.column() col.label("Age Settings:") col.prop(age, "seq_prefix", text="ID") + col.alert = not age.age_name.strip() col.prop(age, "age_name", text="") layout.separator()