# 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 * from PyHSPlasma import * class PlasmaObject(bpy.types.PropertyGroup): def _enabled(self, context): if not self.is_property_set("page"): self._init(context) def _init(self, context): o = self.id_data age = context.scene.world.plasma_age # We want to encourage the pages = layers paradigm. # So, let's see which layers we're on and check for a page whose # suffix matches our layers. We'll take the first match. num_layers = len(o.layers) for page in age.pages: if page.seq_suffix > num_layers: continue if o.layers[page.seq_suffix - 1]: o.plasma_object.page = page.name break enabled = BoolProperty(name="Export", description="Export this as a discrete object", default=False, update=_enabled) page = StringProperty(name="Page", description="Page this object will be exported to") # DEAD - leaving in just in case external code uses it is_inited = BoolProperty(description="DEAD", default=False, options={"HIDDEN"}) @property def ci_type(self): if self.id_data.plasma_modifiers.animation_filter.enabled: return plFilterCoordInterface else: return plCoordinateInterface @property def has_animation_data(self): bo = self.id_data if bo.animation_data is not None: if bo.animation_data.action is not None: return True data = getattr(bo, "data", None) if data is not None: if data.animation_data is not None: if data.animation_data.action is not None: return True return False @property def has_transform_animation(self): bo = self.id_data if bo.animation_data is not None: if bo.animation_data.action is not None: data_paths = frozenset((i.data_path for i in bo.animation_data.action.fcurves)) return {"location", "rotation_euler", "rotation_quaternion", "rotation_axis_angle", "scale"} & data_paths return False @property def subworld(self): bo = self.id_data while bo is not None: if bo.plasma_modifiers.subworld_def.enabled: return bo else: bo = bo.parent return None class PlasmaNet(bpy.types.PropertyGroup): manual_sdl = BoolProperty(name="Override SDL", description="ADVANCED: Manually track high level states on this object", default=False) sdl_names = set() def propagate_synch_options(self, scnobj, synobj): volatile, exclude = set(synobj.volatiles), set(synobj.excludes) if self.manual_sdl: for attr in self.sdl_names: value = getattr(self, attr) if value == "volatile": volatile.add(attr) elif value == "exclude": exclude.add(attr) else: # This SynchedObject may have already excluded or volatile'd everything # If so, bail. if synobj.synchFlags & plSynchedObject.kExcludeAllPersistentState or \ synobj.synchFlags & plSynchedObject.kAllStateIsVolatile: return # Is this a kickable? if scnobj.sim is not None: phys = scnobj.sim.object.physical.object has_kickable = (phys.memberGroup == plSimDefs.kGroupDynamic) else: has_kickable = False # Is there a PythonFileMod? for modKey in scnobj.modifiers: if isinstance(modKey.object, plPythonFileMod): has_pfm = True break else: has_pfm = False # If we have either a kickable or a PFM, the PlasmaMax default is to exclude all the # logic-type stuff for higher level processing (namely, python) if has_kickable or has_pfm: exclude.add("AGMaster") exclude.add("Layer") exclude.add("Responder") exclude.add("Sound") exclude.add("XRegion") else: exclude.update(self.sdl_names) # It doesn't make sense for a state to be both excluded and volatile. # So, if it somehow appears in both lists, we will exclude that state. volatile = volatile.difference(exclude) # Inspect and apply volatile states, if any if len(volatile) == len(self.sdl_names): synobj.synchFlags |= plSynchedObject.kAllStateIsVolatile elif volatile: synobj.synchFlags |= plSynchedObject.kHasVolatileState synobj.volatiles = sorted(volatile) # Inspect and apply exclude states, if any if len(exclude) == len(self.sdl_names): synobj.synchFlags |= plSynchedObject.kExcludeAllPersistentState elif exclude: synobj.synchFlags |= plSynchedObject.kExcludePersistentState synobj.excludes = sorted(exclude) @classmethod def register(cls): def SdlEnumProperty(name): value = bpy.props.EnumProperty(name=name, description="{} state synchronization".format(name), items=[ ("save", "Save to Server", "Save state on the server"), ("volatile", "Volatile on Server", "Throw away state when the age shuts down"), ("exclude", "Don't Send to Server", "Don't synchronize with the server"), ], default="exclude") setattr(PlasmaNet, name, value) PlasmaNet.sdl_names.add(name) agmaster = SdlEnumProperty("AGMaster") avatar = SdlEnumProperty("Avatar") avatar_phys = SdlEnumProperty("AvatarPhysical") clone = SdlEnumProperty("CloneMessage") clothing = SdlEnumProperty("Clothing") layer = SdlEnumProperty("Layer") morph = SdlEnumProperty("MorphSequence") particle = SdlEnumProperty("ParticleSystem") physical = SdlEnumProperty("Physical") responder = SdlEnumProperty("Responder") sound = SdlEnumProperty("Sound") xregion = SdlEnumProperty("XRegion")