diff --git a/korman/operators/__init__.py b/korman/operators/__init__.py
index 3e35852..ef4e5c4 100644
--- a/korman/operators/__init__.py
+++ b/korman/operators/__init__.py
@@ -17,6 +17,7 @@ from . import op_export as exporter
from . import op_lightmap as lightmap
from . import op_modifier as modifier
from . import op_nodes as nodes
+from . import op_sound as sound
from . import op_texture as texture
from . import op_toolbox as toolbox
from . import op_world as world
diff --git a/korman/operators/op_sound.py b/korman/operators/op_sound.py
new file mode 100644
index 0000000..44b6adc
--- /dev/null
+++ b/korman/operators/op_sound.py
@@ -0,0 +1,88 @@
+# 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 *
+
+class SoundOperator:
+ @classmethod
+ def poll(cls, context):
+ return context.scene.render.engine == "PLASMA_GAME"
+
+
+class PlasmaSoundOpenOperator(SoundOperator, bpy.types.Operator):
+ bl_idname = "sound.plasma_open"
+ bl_label = "Load Sound"
+ bl_options = {"INTERNAL"}
+
+ filter_glob = StringProperty(default="*.ogg;*.wav", options={"HIDDEN"})
+ filepath = StringProperty(subtype="FILE_PATH")
+
+ data_path = StringProperty(options={"HIDDEN"})
+ sound_property = StringProperty(options={"HIDDEN"})
+
+ def execute(self, context):
+ # Check to see if the sound exists... Because the sneakily introduced bpy.data.sounds.load
+ # check_existing doesn't tell us if it already exists... dammit...
+ # We don't want to take ownership forcefully if we don't have to.
+ for i in bpy.data.sounds:
+ if self.filepath == i.filepath:
+ sound = i
+ break
+ else:
+ sound = bpy.data.sounds.load(self.filepath)
+ sound.plasma_owned = True
+ sound.use_fake_user = True
+
+ # Now do the stanky leg^H^H^H^H^H^H^H^H^H^H deed and put the sound on the mod
+ dest = eval(self.data_path)
+ setattr(dest, self.sound_property, sound.name)
+ return {"FINISHED"}
+
+ def invoke(self, context, event):
+ context.window_manager.fileselect_add(self)
+ return {"RUNNING_MODAL"}
+
+
+class PlasmaSoundPackOperator(SoundOperator, bpy.types.Operator):
+ bl_idname = "sound.plasma_pack"
+ bl_label = "Pack"
+ bl_options = {"INTERNAL"}
+
+ def execute(self, context):
+ soundemit = context.active_object.plasma_modifiers.soundemit
+ sound = bpy.data.sounds.get(soundemit.sounds[soundemit.active_sound_index].sound_data)
+ sound.pack()
+ return {"FINISHED"}
+
+
+class PlasmaSoundUnpackOperator(SoundOperator, bpy.types.Operator):
+ bl_idname = "sound.plasma_unpack"
+ bl_label = "Unpack"
+ bl_options = {"INTERNAL"}
+
+ method = EnumProperty(name="Method", description="How to unpack",
+ # See blender/makesrna/intern/rna_packedfile.c
+ items=[("USE_LOCAL", "Use local file", "", 5),
+ ("WRITE_LOCAL", "Write Local File (overwrite existing)", "", 4),
+ ("USE_ORIGINAL", "Use Original File", "", 6),
+ ("WRITE_ORIGINAL", "Write Original File (overwrite existing)", "", 3)],
+ options=set())
+
+ def execute(self, context):
+ soundemit = context.active_object.plasma_modifiers.soundemit
+ sound = bpy.data.sounds.get(soundemit.sounds[soundemit.active_sound_index].sound_data)
+ sound.unpack(self.method)
+ return {"FINISHED"}
diff --git a/korman/properties/modifiers/__init__.py b/korman/properties/modifiers/__init__.py
index 6223ac8..938e333 100644
--- a/korman/properties/modifiers/__init__.py
+++ b/korman/properties/modifiers/__init__.py
@@ -22,6 +22,7 @@ from .logic import *
from .physics import *
from .region import *
from .render import *
+from .sound import *
from .water import *
class PlasmaModifiers(bpy.types.PropertyGroup):
diff --git a/korman/properties/modifiers/sound.py b/korman/properties/modifiers/sound.py
new file mode 100644
index 0000000..b315f91
--- /dev/null
+++ b/korman/properties/modifiers/sound.py
@@ -0,0 +1,64 @@
+# 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 bpy.app.handlers import persistent
+from PyHSPlasma import *
+
+from .base import PlasmaModifierProperties
+from ...exporter import ExportError
+
+class PlasmaSound(bpy.types.PropertyGroup):
+ enabled = BoolProperty(name="Enabled", default=True, options=set())
+ sound_data = StringProperty(name="Sound", description="Sound Datablock", options=set())
+
+
+class PlasmaSoundEmitter(PlasmaModifierProperties):
+ pl_id = "soundemit"
+
+ bl_category = "Logic"
+ bl_label = "Sound Emitter"
+ bl_description = "Point at which sound(s) are played"
+ bl_icon = "SPEAKER"
+
+ sounds = CollectionProperty(type=PlasmaSound)
+ active_sound_index = IntProperty(options={"HIDDEN"})
+
+ def export(self, exporter, bo, so):
+ pass
+
+ @classmethod
+ def register(cls):
+ bpy.types.Sound.plasma_owned = BoolProperty(default=False, options={"HIDDEN"})
+
+ @property
+ def requires_actor(self):
+ return True
+
+
+@persistent
+def _toss_orphaned_sounds(scene):
+ used_sounds = set()
+ for i in bpy.data.objects:
+ soundemit = i.plasma_modifiers.soundemit
+ used_sounds.update((j.sound_data for j in soundemit.sounds))
+ for i in bpy.data.sounds:
+ if i.plasma_owned and i.name not in used_sounds:
+ i.use_fake_user = False
+ i.user_clear()
+
+# collects orphaned Plasma owned sound datablocks
+bpy.app.handlers.save_pre.append(_toss_orphaned_sounds)
diff --git a/korman/ui/modifiers/__init__.py b/korman/ui/modifiers/__init__.py
index 5cf2fe7..ffbf492 100644
--- a/korman/ui/modifiers/__init__.py
+++ b/korman/ui/modifiers/__init__.py
@@ -19,4 +19,5 @@ from .logic import *
from .physics import *
from .region import *
from .render import *
+from .sound import *
from .water import *
diff --git a/korman/ui/modifiers/sound.py b/korman/ui/modifiers/sound.py
new file mode 100644
index 0000000..feabe39
--- /dev/null
+++ b/korman/ui/modifiers/sound.py
@@ -0,0 +1,60 @@
+# 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
+
+class SoundListUI(bpy.types.UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0):
+ if item.sound_data:
+ sound = bpy.data.sounds.get(item.sound_data)
+ icon = "SOUND" if sound is not None else "ERROR"
+ layout.prop(item, "sound_data", emboss=False, icon=icon, text="")
+ layout.prop(item, "enabled", text="")
+ else:
+ layout.label("[Empty]")
+
+
+def soundemit(modifier, layout, context):
+ row = layout.row()
+ row.template_list("SoundListUI", "sounds", modifier, "sounds", modifier, "active_sound_index",
+ rows=2, maxrows=3)
+ col = row.column(align=True)
+ op = col.operator("object.plasma_modifier_collection_add", icon="ZOOMIN", text="")
+ op.modifier = modifier.pl_id
+ op.collection = "sounds"
+ op = col.operator("object.plasma_modifier_collection_remove", icon="ZOOMOUT", text="")
+ op.modifier = modifier.pl_id
+ op.collection = "sounds"
+ op.index = modifier.active_sound_index
+
+ try:
+ sound = modifier.sounds[modifier.active_sound_index]
+ except:
+ pass
+ else:
+ # Sound datablock picker
+ row = layout.row(align=True)
+ row.prop_search(sound, "sound_data", bpy.data, "sounds", text="")
+ open_op = row.operator("sound.plasma_open", icon="FILESEL", text="")
+ open_op.data_path = repr(sound)
+ open_op.sound_property = "sound_data"
+
+ # Pack/Unpack
+ data = bpy.data.sounds.get(sound.sound_data)
+ if data is not None:
+ if data.packed_file is None:
+ row.operator("sound.plasma_pack", icon="UGLYPACKAGE", text="")
+ else:
+ row.operator_menu_enum("sound.plasma_unpack", "method", icon="PACKAGE", text="")