diff --git a/d2toolbox/__init__.py b/d2toolbox/__init__.py index b396f31..412b2ae 100644 --- a/d2toolbox/__init__.py +++ b/d2toolbox/__init__.py @@ -1,11 +1,13 @@ if "bpy" in locals(): import importlib importlib.reload(globals) + importlib.reload(properties) importlib.reload(operators) importlib.reload(panels) importlib.reload(preferences) else: from . import globals + from . import properties from . import operators from . import panels from . import preferences @@ -13,6 +15,7 @@ else: import bpy classes = ( + *properties.classes, *operators.classes, *panels.classes, preferences.D2ToolboxPreferences, @@ -22,6 +25,8 @@ def register(): for cls in classes: bpy.utils.register_class(cls) + bpy.types.Scene.d2toolbox = bpy.props.PointerProperty(type=properties.D2ToolboxProperties) + if globals.getManifest() is None and bpy.app.online_access: try: globals.fetchAndSaveManifest() @@ -29,6 +34,8 @@ def register(): print(f"D2Toolbox: failed to fetch manifest on load: {e}") def unregister(): + del bpy.types.Scene.d2toolbox + for cls in reversed(classes): bpy.utils.unregister_class(cls) diff --git a/d2toolbox/operators.py b/d2toolbox/operators.py index 5c4667f..237a66b 100644 --- a/d2toolbox/operators.py +++ b/d2toolbox/operators.py @@ -23,7 +23,19 @@ class D2TOOLBOX_OT_refetch_manifest(bpy.types.Operator): self.report({"INFO"}, "Manifest refreshed") return {"FINISHED"} +class D2TOOLBOX_OT_load_set(bpy.types.Operator): + """Load the selected armor set (stub).""" + bl_idname = "d2toolbox.load_set" + bl_label = "Load Set" + bl_options = {"REGISTER"} + + def execute(self, context): + # Stub for now + self.report({"INFO"}, "Load Set (stub)") + return {"FINISHED"} + classes = ( D2TOOLBOX_OT_hello, - D2TOOLBOX_OT_refetch_manifest + D2TOOLBOX_OT_refetch_manifest, + D2TOOLBOX_OT_load_set ) diff --git a/d2toolbox/panels.py b/d2toolbox/panels.py index 8ef1c22..d56c40c 100644 --- a/d2toolbox/panels.py +++ b/d2toolbox/panels.py @@ -17,6 +17,40 @@ class D2TOOLBOX_PT_main(bpy.types.Panel): return layout = self.layout + + props = context.scene.d2toolbox + + layout.label(text="Armor Set Loader") + + layout.label(text="Class") + row = layout.row(align=True) + row.prop_enum(props, "d2_class", "hunter") + row.prop_enum(props, "d2_class", "warlock") + row.prop_enum(props, "d2_class", "titan") + + layout.label(text="Sex") + row = layout.row(align=True) + row.prop_enum(props, "d2_sex", "male") + row.prop_enum(props, "d2_sex", "female") + + ready = props.d2_class != "NONE" and props.d2_sex != "NONE" + col = layout.column() + col.enabled = ready # greyed out until Class + Sex chosen + col.prop(props, "slot_head") + col.prop(props, "slot_arms") + col.prop(props, "slot_chest") + col.prop(props, "slot_legs") + col.prop(props, "slot_class_item") + + all_selected = ready and all( + getattr(props, a) != "NONE" + for a in ("slot_head", "slot_arms", "slot_chest", "slot_legs", "slot_class_item") + ) + sub = layout.column() + sub.enabled = all_selected # greyed out until all 5 slots chosen + sub.operator("d2toolbox.load_set") + + layout.separator() layout.label(text="Miscellaneous") layout.operator("d2toolbox.refetch_manifest") diff --git a/d2toolbox/properties.py b/d2toolbox/properties.py new file mode 100644 index 0000000..6101108 --- /dev/null +++ b/d2toolbox/properties.py @@ -0,0 +1,53 @@ +import bpy +from . import globals + +# Blender garbage-collects strings returned by dynamic EnumProperty `items` +# callbacks unless a Python reference is kept. Cache each built list here, +# keyed by (class, sex), to keep them alive. +_armor_items_cache = {} + +CLASS_ITEMS = [ + ("NONE", "", ""), + ("hunter", "Hunter", ""), + ("warlock", "Warlock", ""), + ("titan", "Titan", ""), +] + +SEX_ITEMS = [ + ("NONE", "", ""), + ("male", "Male", ""), + ("female", "Female", ""), +] + +_SLOT_ATTRS = ("slot_head", "slot_arms", "slot_chest", "slot_legs", "slot_class_item") + +def _armor_items(self, context): + key = (self.d2_class, self.d2_sex) + items = [("NONE", "— None —", "")] + if self.d2_class != "NONE" and self.d2_sex != "NONE": + manifest = globals.getManifest() + entries = (manifest.get(self.d2_class, {}).get(self.d2_sex, []) if manifest else []) or [] + for i, entry in enumerate(entries): + name = entry.get("name", "Unknown") + season = entry.get("season", "") + label = f"{name} - {season}" if season else name + items.append((str(i), label, "")) + _armor_items_cache[key] = items + return items + +def _reset_slots(self, context): + for attr in _SLOT_ATTRS: + self[attr] = 0 # index 0 == 'NONE' + +class D2ToolboxProperties(bpy.types.PropertyGroup): + d2_class: bpy.props.EnumProperty(name="Class", items=CLASS_ITEMS, default="NONE", update=_reset_slots) + d2_sex: bpy.props.EnumProperty(name="Sex", items=SEX_ITEMS, default="NONE", update=_reset_slots) + slot_head: bpy.props.EnumProperty(name="Head", items=_armor_items) + slot_arms: bpy.props.EnumProperty(name="Arms", items=_armor_items) + slot_chest: bpy.props.EnumProperty(name="Chest", items=_armor_items) + slot_legs: bpy.props.EnumProperty(name="Legs", items=_armor_items) + slot_class_item: bpy.props.EnumProperty(name="Class Item", items=_armor_items) + +classes = ( + D2ToolboxProperties, +)