Browse Source

Add prop attachment to maids

Works similarly to studio mode
habeebweeb 4 years ago
parent
commit
6deae4429c

+ 24 - 1
COM3D2.MeidoPhotoStudio.Plugin/Config/MeidoPhotoStudio/Translations/en/translation.ui.json

@@ -283,12 +283,35 @@
         "tail": "Tail",
         "genitals": "Genitals"
     },
+    "attachPropPane": {
+        "keepWorldPosition": "Keep World Position",
+        "header": "Attach Point",
+        "head": "Head",
+        "neck": "Neck",
+        "upperArmL": "Shoulder L",
+        "upperArmR": "Shoulder R",
+        "forearmL": "Forearm L",
+        "forearmR": "Forearm R",
+        "muneL": "Breast Left",
+        "muneR": "Breast Right",
+        "handL": "Hand Left",
+        "handR": "Hand Right",
+        "pelvis": "Pelvis",
+        "thighL": "Thigh Left",
+        "thighR": "Thigh Right",
+        "calfL": "Calf Left",
+        "calfR": "Calf Right",
+        "footL": "Foot Left",
+        "footR": "Foot Right"
+    },
     "messageWindow": {
         "name": "Name",
         "fontSize": "Font Size",
         "okButton": "OK"
     },
     "systemMessage": {
-        "initializing": "Initializing"
+        "initializing": "Initializing",
+        "noMaids": "No Maids",
+        "noProps": "No Props"
     }
 }

+ 1 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/DragDogu.cs

@@ -17,6 +17,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
         public event EventHandler Select;
         public bool DeleteMe { get; private set; }
         public string Name => Dogu.name;
+        public DragPointManager.AttachPointInfo attachPointInfo = DragPointManager.AttachPointInfo.Empty;
         public bool keepDogu = false;
         public float scaleFactor = 1f;
 

+ 234 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/BackgroundWindow2Panes/AttachPropPane.cs

@@ -0,0 +1,234 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+namespace COM3D2.MeidoPhotoStudio.Plugin
+{
+    using static DragPointManager;
+    internal class AttachPropPane : BasePane
+    {
+        private PropManager propManager;
+        private MeidoManager meidoManager;
+        private Dictionary<AttachPoint, Toggle> Toggles = new Dictionary<AttachPoint, Toggle>();
+        private static readonly Dictionary<AttachPoint, string> toggleTranslation =
+            new Dictionary<AttachPoint, string>()
+            {
+                [AttachPoint.Head] = "head",
+                [AttachPoint.Neck] = "neck",
+                [AttachPoint.UpperArmL] = "upperArmL",
+                [AttachPoint.UpperArmR] = "upperArmR",
+                [AttachPoint.ForearmL] = "forearmL",
+                [AttachPoint.ForearmR] = "forearmR",
+                [AttachPoint.MuneL] = "muneL",
+                [AttachPoint.MuneR] = "muneR",
+                [AttachPoint.HandL] = "handL",
+                [AttachPoint.HandR] = "handR",
+                [AttachPoint.Pelvis] = "pelvis",
+                [AttachPoint.ThighL] = "thighL",
+                [AttachPoint.ThighR] = "thighR",
+                [AttachPoint.CalfL] = "calfL",
+                [AttachPoint.CalfR] = "calfR",
+                [AttachPoint.FootL] = "footL",
+                [AttachPoint.FootR] = "footR",
+            };
+        private Toggle keepWorldPositionToggle;
+        private Button previousMaidButton;
+        private Button nextMaidButton;
+        private Dropdown meidoDropdown;
+        private Button previousDoguButton;
+        private Button nextDoguButton;
+        private Dropdown doguDropdown;
+        private bool meidoDropdownActive = false;
+        private bool doguDropdownActive = false;
+        private bool PaneActive => meidoDropdownActive && doguDropdownActive;
+        private string header;
+        private int selectedMaid = 0;
+
+        public AttachPropPane(MeidoManager meidoManager, PropManager propManager)
+        {
+            this.header = Translation.Get("attachPropPane", "header");
+            this.propManager = propManager;
+            this.meidoManager = meidoManager;
+
+            this.propManager.DoguListChange += (s, a) => SetDoguDropdown();
+            this.meidoManager.EndCallMeidos += (s, a) => SetMeidoDropdown();
+
+            this.meidoDropdown = new Dropdown(new[] { Translation.Get("systemMessage", "noMaids") });
+            this.meidoDropdown.SelectionChange += (s, a) => SwitchMaid();
+
+            this.previousMaidButton = new Button("<");
+            this.previousMaidButton.ControlEvent += (s, a) => this.meidoDropdown.Step(-1);
+
+            this.nextMaidButton = new Button(">");
+            this.nextMaidButton.ControlEvent += (s, a) => this.meidoDropdown.Step(1);
+
+            this.doguDropdown = new Dropdown(new[] { Translation.Get("systemMessage", "noProps") });
+            this.doguDropdown.SelectionChange += (s, a) => SwitchDogu();
+
+            this.previousDoguButton = new Button("<");
+            this.previousDoguButton.ControlEvent += (s, a) => this.doguDropdown.Step(-1);
+
+            this.nextDoguButton = new Button(">");
+            this.nextDoguButton.ControlEvent += (s, a) => this.doguDropdown.Step(1);
+
+            this.keepWorldPositionToggle = new Toggle(Translation.Get("attachPropPane", "keepWorldPosition"));
+
+            foreach (AttachPoint attachPoint in Enum.GetValues(typeof(AttachPoint)))
+            {
+                if (attachPoint == AttachPoint.None) continue;
+                AttachPoint point = attachPoint;
+                Toggle toggle = new Toggle(Translation.Get("attachPropPane", toggleTranslation[point]));
+                toggle.ControlEvent += (s, a) =>
+                {
+                    if (this.updating) return;
+                    ChangeAttachPoint(point);
+                };
+                Toggles[point] = toggle;
+            }
+        }
+
+        protected override void ReloadTranslation()
+        {
+            this.header = Translation.Get("attachPropPane", "header");
+            this.keepWorldPositionToggle.Label = Translation.Get("attachPropPane", "keepWorldPosition");
+            foreach (AttachPoint attachPoint in Enum.GetValues(typeof(AttachPoint)))
+            {
+                if (attachPoint == AttachPoint.None) continue;
+                Toggles[attachPoint].Label = Translation.Get("attachPropPane", toggleTranslation[attachPoint]);
+            }
+        }
+
+        public override void Draw()
+        {
+            float arrowButtonSize = 30;
+            GUILayoutOption[] arrowLayoutOptions = {
+                GUILayout.Width(arrowButtonSize),
+                GUILayout.Height(arrowButtonSize)
+            };
+
+            float dropdownButtonHeight = arrowButtonSize;
+            float dropdownButtonWidth = 153f;
+            GUILayoutOption[] dropdownLayoutOptions = new GUILayoutOption[] {
+                GUILayout.Height(dropdownButtonHeight),
+                GUILayout.Width(dropdownButtonWidth)
+            };
+
+            MiscGUI.Header(this.header);
+            MiscGUI.WhiteLine();
+
+            GUI.enabled = PaneActive;
+
+            meidoDropdown.Draw(dropdownLayoutOptions);
+
+            GUILayout.BeginHorizontal();
+            doguDropdown.Draw(dropdownLayoutOptions);
+            previousDoguButton.Draw(arrowLayoutOptions);
+            nextDoguButton.Draw(arrowLayoutOptions);
+            GUILayout.EndHorizontal();
+
+            keepWorldPositionToggle.Draw();
+
+            DrawToggleGroup(AttachPoint.Head, AttachPoint.Neck);
+            DrawToggleGroup(AttachPoint.UpperArmL, AttachPoint.UpperArmR);
+            DrawToggleGroup(AttachPoint.ForearmL, AttachPoint.ForearmR);
+            DrawToggleGroup(AttachPoint.MuneL, AttachPoint.MuneR);
+            DrawToggleGroup(AttachPoint.HandL, AttachPoint.Pelvis, AttachPoint.HandR);
+            DrawToggleGroup(AttachPoint.ThighL, AttachPoint.ThighR);
+            DrawToggleGroup(AttachPoint.CalfL, AttachPoint.CalfR);
+            DrawToggleGroup(AttachPoint.FootL, AttachPoint.FootR);
+
+            GUI.enabled = true;
+        }
+
+        private void DrawToggleGroup(params AttachPoint[] attachPoints)
+        {
+            GUILayout.BeginHorizontal();
+            GUILayout.FlexibleSpace();
+            foreach (AttachPoint point in attachPoints)
+            {
+                Toggles[point].Draw();
+            }
+            GUILayout.FlexibleSpace();
+            GUILayout.EndHorizontal();
+        }
+
+        private void SetAttachPointToggle(AttachPoint point, bool value)
+        {
+            this.updating = true;
+            foreach (KeyValuePair<AttachPoint, Toggle> kvp in Toggles)
+            {
+                Toggle toggle = kvp.Value;
+                toggle.Value = false;
+            }
+            if (point != AttachPoint.None) Toggles[point].Value = value;
+            this.updating = false;
+        }
+
+        private void ChangeAttachPoint(AttachPoint point)
+        {
+            bool toggleValue = point == AttachPoint.None ? false : Toggles[point].Value;
+            SetAttachPointToggle(point, toggleValue);
+
+            Meido meido = null;
+
+            if (point != AttachPoint.None)
+            {
+                meido = Toggles[point].Value
+                    ? this.meidoManager.ActiveMeidoList[this.meidoDropdown.SelectedItemIndex]
+                    : null;
+            }
+
+            this.propManager.AttachProp(
+                this.doguDropdown.SelectedItemIndex, point, meido, this.keepWorldPositionToggle.Value
+            );
+        }
+
+        private void SwitchMaid()
+        {
+            if (updating || selectedMaid == this.meidoDropdown.SelectedItemIndex) return;
+            selectedMaid = this.meidoDropdown.SelectedItemIndex;
+            DragDogu dragDogu = this.propManager.GetDogu(this.doguDropdown.SelectedItemIndex);
+            if (dragDogu != null)
+            {
+                if (dragDogu.attachPointInfo.AttachPoint == AttachPoint.None) return;
+                ChangeAttachPoint(dragDogu.attachPointInfo.AttachPoint);
+            }
+        }
+
+        private void SwitchDogu()
+        {
+            if (updating) return;
+            DragDogu dragDogu = this.propManager.GetDogu(this.doguDropdown.SelectedItemIndex);
+            if (dragDogu != null) SetAttachPointToggle(dragDogu.attachPointInfo.AttachPoint, true);
+        }
+
+        private void SetDoguDropdown()
+        {
+            if (this.propManager.DoguCount == 0)
+            {
+                SetAttachPointToggle(AttachPoint.Head, false);
+            }
+            int index = Mathf.Clamp(this.doguDropdown.SelectedItemIndex, 0, this.propManager.DoguCount);
+
+            this.doguDropdown.SetDropdownItems(this.propManager.PropNameList, index);
+
+            doguDropdownActive = this.propManager.DoguCount != 0;
+        }
+
+        private void SetMeidoDropdown()
+        {
+            if (this.meidoManager.ActiveMeidoList.Count == 0)
+            {
+                SetAttachPointToggle(AttachPoint.Head, false);
+            }
+            int index = Mathf.Clamp(this.meidoDropdown.SelectedItemIndex, 0, this.meidoManager.ActiveMeidoList.Count);
+
+            this.updating = true;
+            this.meidoDropdown.SetDropdownItems(this.meidoManager.ActiveMeidoNameList, index);
+            this.updating = false;
+
+            meidoDropdownActive = this.meidoManager.HasActiveMeido;
+        }
+    }
+}

+ 10 - 2
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/MainWindowPanes/BG2WindowPane.cs

@@ -6,17 +6,24 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
 {
     internal class BG2WindowPane : BaseWindowPane
     {
+        private EnvironmentManager environmentManager;
+        private MeidoManager meidoManager;
         private PropsPane propsPane;
-        EnvironmentManager environmentManager;
-        public BG2WindowPane(EnvironmentManager environmentManager)
+        private AttachPropPane attachPropPane;
+
+        public BG2WindowPane(MeidoManager meidoManager, EnvironmentManager environmentManager)
         {
             this.environmentManager = environmentManager;
+            this.meidoManager = meidoManager;
 
             this.propsPane = new PropsPane(this.environmentManager.PropManager);
+            this.attachPropPane = new AttachPropPane(this.meidoManager, this.environmentManager.PropManager);
         }
+
         public override void Draw()
         {
             this.propsPane.Draw();
+            this.attachPropPane.Draw();
         }
 
         public override void UpdatePanes()
@@ -24,6 +31,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             if (ActiveWindow)
             {
                 this.propsPane.UpdatePane();
+                this.attachPropPane.UpdatePane();
             }
         }
     }

+ 100 - 50
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/Managers/DragPointManager.cs

@@ -43,6 +43,35 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             Toe1R, Toe11R, Toe1NubR,
             Toe2R, Toe21R, Toe2NubR
         }
+        public enum AttachPoint
+        {
+            None,
+            Head, Neck, UpperArmL, UpperArmR, ForearmL, ForearmR, MuneL, MuneR, HandL, HandR,
+            Pelvis, ThighL, ThighR, CalfL, CalfR, FootL, FootR
+        }
+
+        private static readonly Dictionary<AttachPoint, Bone> PointToBone = new Dictionary<AttachPoint, Bone>()
+        {
+            [AttachPoint.Head] = Bone.Head,
+            [AttachPoint.Neck] = Bone.HeadNub,
+            [AttachPoint.UpperArmL] = Bone.UpperArmL,
+            [AttachPoint.UpperArmR] = Bone.UpperArmR,
+            [AttachPoint.ForearmL] = Bone.ForearmL,
+            [AttachPoint.ForearmR] = Bone.ForearmR,
+            [AttachPoint.MuneL] = Bone.MuneL,
+            [AttachPoint.MuneR] = Bone.MuneR,
+            [AttachPoint.HandL] = Bone.HandL,
+            [AttachPoint.HandR] = Bone.HandR,
+            [AttachPoint.Pelvis] = Bone.Pelvis,
+            [AttachPoint.ThighL] = Bone.ThighL,
+            [AttachPoint.ThighR] = Bone.ThighR,
+            [AttachPoint.CalfL] = Bone.CalfL,
+            [AttachPoint.CalfR] = Bone.CalfR,
+            [AttachPoint.FootL] = Bone.FootL,
+            [AttachPoint.FootR] = Bone.FootR,
+        };
+
+
         private static readonly Dictionary<IKMode, Bone[]> IKGroup = new Dictionary<IKMode, Bone[]>()
         {
             [IKMode.None] = new[]
@@ -234,6 +263,12 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             ikModeOld = ikMode;
         }
 
+        public Transform GetAttachPointTransform(AttachPoint point)
+        {
+            if (point == AttachPoint.None) return null;
+            return BoneTransform[PointToBone[point]];
+        }
+
         private void Initialize(object sender, EventArgs args)
         {
             meido.BodyLoad -= Initialize;
@@ -429,43 +464,40 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             };
 
             // Cube Dragpoint
-            DragPoint[Bone.Cube] =
-                BaseDrag.MakeDragPoint(PrimitiveType.Cube, Vector3.one * 0.12f, BaseDrag.Blue)
-                .AddComponent<DragBody>()
-                .Initialize(meido,
-                    () => maid.transform.position,
-                    () => maid.transform.eulerAngles
-                );
+            DragPoint[Bone.Cube] = BaseDrag.MakeDragPoint<DragBody>(
+                PrimitiveType.Cube, Vector3.one * 0.12f, BaseDrag.Blue
+            ).Initialize(meido,
+                () => maid.transform.position,
+                () => maid.transform.eulerAngles
+            );
             DragBody dragCube = (DragBody)DragPoint[Bone.Cube];
             dragCube.Scale += OnSetDragPointScale;
             dragCube.DragPointVisible = true;
             // TODO: Make gizmos work on cube
 
             // Body Dragpoint
-            DragPoint[Bone.Body] =
-                BaseDrag.MakeDragPoint(PrimitiveType.Capsule, new Vector3(0.2f, 0.3f, 0.24f), BaseDrag.LightBlue)
-                .AddComponent<DragBody>()
-                .Initialize(meido,
-                    () => new Vector3(
-                        (BoneTransform[Bone.Hip].position.x + BoneTransform[Bone.Spine0a].position.x) / 2f,
-                        (BoneTransform[Bone.Spine1].position.y + BoneTransform[Bone.Spine0a].position.y) / 2f,
-                        (BoneTransform[Bone.Spine0a].position.z + BoneTransform[Bone.Hip].position.z) / 2f
-                    ),
-                    () => new Vector3(
-                        BoneTransform[Bone.Spine0a].transform.eulerAngles.x,
-                        BoneTransform[Bone.Spine0a].transform.eulerAngles.y,
-                        BoneTransform[Bone.Spine0a].transform.eulerAngles.z + 90f
-                    )
+            DragPoint[Bone.Body] = BaseDrag.MakeDragPoint<DragBody>(
+                PrimitiveType.Capsule, new Vector3(0.2f, 0.3f, 0.24f), BaseDrag.LightBlue
+            ).Initialize(meido,
+                () => new Vector3(
+                    (BoneTransform[Bone.Hip].position.x + BoneTransform[Bone.Spine0a].position.x) / 2f,
+                    (BoneTransform[Bone.Spine1].position.y + BoneTransform[Bone.Spine0a].position.y) / 2f,
+                    (BoneTransform[Bone.Spine0a].position.z + BoneTransform[Bone.Hip].position.z) / 2f
+                ),
+                () => new Vector3(
+                    BoneTransform[Bone.Spine0a].transform.eulerAngles.x,
+                    BoneTransform[Bone.Spine0a].transform.eulerAngles.y,
+                    BoneTransform[Bone.Spine0a].transform.eulerAngles.z + 90f
+                )
             );
             DragBody dragBody = (DragBody)DragPoint[Bone.Body];
             dragBody.Select += OnSelectBody;
             dragBody.Scale += OnSetDragPointScale;
 
             // Head Dragpoint
-            DragPoint[Bone.Head] =
-                BaseDrag.MakeDragPoint(PrimitiveType.Sphere, new Vector3(0.2f, 0.24f, 0.2f), BaseDrag.LightBlue)
-                .AddComponent<DragHead>()
-                .Initialize(BoneTransform[Bone.Neck], meido,
+            DragPoint[Bone.Head] = BaseDrag.MakeDragPoint<DragHead>(
+                PrimitiveType.Sphere, new Vector3(0.2f, 0.24f, 0.2f), BaseDrag.LightBlue
+            ).Initialize(BoneTransform[Bone.Neck], meido,
                 () => new Vector3(
                     BoneTransform[Bone.Head].position.x,
                     (BoneTransform[Bone.Head].position.y * 1.2f + BoneTransform[Bone.HeadNub].position.y * 0.8f) / 2f,
@@ -475,16 +507,15 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                     BoneTransform[Bone.Head].eulerAngles.x,
                     BoneTransform[Bone.Head].eulerAngles.y,
                     BoneTransform[Bone.Head].eulerAngles.z + 90f
-
                 )
             );
             DragHead dragHead = (DragHead)DragPoint[Bone.Head];
             dragHead.Select += OnSelectFace;
 
             // Torso Dragpoint
-            DragPoint[Bone.Torso] =
-                BaseDrag.MakeDragPoint(PrimitiveType.Capsule, new Vector3(0.2f, 0.19f, 0.24f), BaseDrag.LightBlue)
-                .AddComponent<DragTorso>();
+            DragPoint[Bone.Torso] = BaseDrag.MakeDragPoint<DragTorso>(
+                PrimitiveType.Capsule, new Vector3(0.2f, 0.19f, 0.24f), BaseDrag.LightBlue
+            );
             Transform spineTrans1 = BoneTransform[Bone.Spine1];
             Transform spineTrans2 = BoneTransform[Bone.Spine1a];
             Transform[] spineParts = new Transform[4] {
@@ -508,9 +539,9 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             );
 
             // Pelvis Dragpoint
-            DragPoint[Bone.Pelvis] =
-                BaseDrag.MakeDragPoint(PrimitiveType.Capsule, new Vector3(0.2f, 0.15f, 0.24f), BaseDrag.LightBlue)
-                .AddComponent<DragPelvis>();
+            DragPoint[Bone.Pelvis] = BaseDrag.MakeDragPoint<DragPelvis>(
+                PrimitiveType.Capsule, new Vector3(0.2f, 0.15f, 0.24f), BaseDrag.LightBlue
+            );
             Transform pelvisTrans = BoneTransform[Bone.Pelvis];
             Transform spineTrans = BoneTransform[Bone.Spine];
             DragPelvis dragPelvis = (DragPelvis)DragPoint[Bone.Pelvis];
@@ -528,9 +559,9 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             );
 
             // Left Mune Dragpoint
-            DragPoint[Bone.MuneL] =
-                BaseDrag.MakeDragPoint(PrimitiveType.Sphere, Vector3.one * 0.12f, BaseDrag.LightBlue)
-                .AddComponent<DragMune>();
+            DragPoint[Bone.MuneL] = BaseDrag.MakeDragPoint<DragMune>(
+                PrimitiveType.Sphere, Vector3.one * 0.12f, BaseDrag.LightBlue
+            );
             Transform[] muneIKChainL = new Transform[3] {
                 BoneTransform[Bone.MuneL],
                 BoneTransform[Bone.MuneL],
@@ -543,9 +574,9 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             );
 
             // Right Mune Dragpoint
-            DragPoint[Bone.MuneR] =
-                BaseDrag.MakeDragPoint(PrimitiveType.Sphere, Vector3.one * 0.12f, BaseDrag.LightBlue)
-                .AddComponent<DragMune>();
+            DragPoint[Bone.MuneR] = BaseDrag.MakeDragPoint<DragMune>(
+                PrimitiveType.Sphere, Vector3.one * 0.12f, BaseDrag.LightBlue
+            );
             Transform[] muneIKChainR = new Transform[3] {
                 BoneTransform[Bone.MuneR],
                 BoneTransform[Bone.MuneR],
@@ -655,21 +686,21 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             for (Bone bone = Bone.Neck; bone <= Bone.ThighR; ++bone)
             {
                 Transform pos = BoneTransform[bone];
-                DragPoint[bone] = BaseDrag.MakeDragPoint(PrimitiveType.Sphere, Vector3.one * 0.04f, BaseDrag.LightBlue)
-                    .AddComponent<DragSpine>()
-                    .Initialize(BoneTransform[bone], false, meido,
-                        () => pos.position,
-                        () => Vector3.zero
-                    );
+                DragPoint[bone] = BaseDrag.MakeDragPoint<DragSpine>(
+                    PrimitiveType.Sphere, Vector3.one * 0.04f, BaseDrag.LightBlue
+                ).Initialize(BoneTransform[bone], false, meido,
+                    () => pos.position,
+                    () => Vector3.zero
+                );
             }
 
             // Hip DragPoint
-            DragPoint[Bone.Hip] = BaseDrag.MakeDragPoint(PrimitiveType.Cube, Vector3.one * 0.045f, BaseDrag.LightBlue)
-                .AddComponent<DragSpine>()
-                .Initialize(BoneTransform[Bone.Hip], true, meido,
-                    () => BoneTransform[Bone.Hip].position,
-                    () => Vector3.zero
-                );
+            DragPoint[Bone.Hip] = BaseDrag.MakeDragPoint<DragSpine>(
+                PrimitiveType.Cube, Vector3.one * 0.045f, BaseDrag.LightBlue
+            ).Initialize(BoneTransform[Bone.Hip], true, meido,
+                () => BoneTransform[Bone.Hip].position,
+                () => Vector3.zero
+            );
 
             MakeFingerDragPoint(Bone.Finger0L, Bone.Finger4R, 4);
             MakeFingerDragPoint(Bone.Toe0L, Bone.Toe2R, 3);
@@ -806,5 +837,24 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                 return new DragInfo(bone, true, true, true);
             }
         }
+
+        public struct AttachPointInfo
+        {
+            public DragPointManager.AttachPoint AttachPoint { get; }
+            public string MaidGuid { get; }
+            public int MaidIndex { get; }
+            public static AttachPointInfo Empty
+            {
+                get => new AttachPointInfo(DragPointManager.AttachPoint.None, String.Empty, -1);
+            }
+
+            public AttachPointInfo(DragPointManager.AttachPoint attachPoint, string maidGuid, int maidIndex)
+            {
+                this.AttachPoint = attachPoint;
+                this.MaidGuid = maidGuid;
+                this.MaidIndex = maidIndex;
+            }
+
+        }
     }
 }

+ 2 - 2
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/Managers/EnvironmentManager.cs

@@ -59,9 +59,9 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             None, Transform
         }
 
-        public EnvironmentManager()
+        public EnvironmentManager(MeidoManager meidoManager)
         {
-            PropManager = new PropManager();
+            PropManager = new PropManager(meidoManager);
             LightManager = new LightManager();
             EffectManager = new EffectManager();
         }

+ 29 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/Managers/MeidoManager.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Linq;
 using UnityEngine;
 
 namespace COM3D2.MeidoPhotoStudio.Plugin
@@ -19,6 +20,15 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
         public event EventHandler EndCallMeidos;
         public event EventHandler BeginCallMeidos;
         private int selectedMeido = 0;
+        public string[] ActiveMeidoNameList
+        {
+            get
+            {
+                return ActiveMeidoList.Count == 0
+                    ? new[] { Translation.Get("systemMessage", "noMaids") }
+                    : ActiveMeidoList.Select(meido => $"{meido.FirstName} {meido.LastName}").ToArray();
+            }
+        }
         public int SelectedMeido
         {
             get => selectedMeido;
@@ -101,6 +111,25 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             }, false);
         }
 
+        public Meido GetMeido(string guid)
+        {
+            foreach (Meido meido in ActiveMeidoList)
+            {
+                if (meido.Maid.status.guid == guid) return meido;
+            }
+
+            return null;
+        }
+
+        public Meido GetMeido(int activeIndex)
+        {
+            if (activeIndex >= 0 && activeIndex < ActiveMeidoList.Count)
+            {
+                return ActiveMeidoList[activeIndex];
+            }
+            return null;
+        }
+
         private void UndressAll()
         {
             if (!HasActiveMeido) return;

+ 107 - 5
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/Managers/PropManager.cs

@@ -9,6 +9,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
 {
     internal class PropManager
     {
+        private MeidoManager meidoManager;
         private static bool cubeActive = true;
         public static bool CubeActive
         {
@@ -45,6 +46,24 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
         {
             None, Move, Rotate, Scale, Delete, Other
         }
+        public int DoguCount => doguList.Count;
+        public event EventHandler DoguListChange;
+        public string[] PropNameList
+        {
+            get
+            {
+                return doguList.Count == 0
+                    ? new[] { Translation.Get("systemMessage", "noProps") }
+                    : doguList.Select(dogu => dogu.Name).ToArray();
+            }
+        }
+
+        public PropManager(MeidoManager meidoManager)
+        {
+            this.meidoManager = meidoManager;
+            this.meidoManager.BeginCallMeidos += DetachProps;
+            this.meidoManager.EndCallMeidos += ReattachProps;
+        }
 
         public void Activate()
         {
@@ -55,6 +74,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
         {
             foreach (DragDogu dogu in doguList)
             {
+                dogu.Delete -= DeleteDogu;
                 GameObject.Destroy(dogu.gameObject);
             }
             doguList.Clear();
@@ -106,13 +126,18 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
         {
             // TODO: Add a couple more things to ignore list
             GameObject dogu = null;
-            string doguName = assetName;
+            string doguName = Translation.Get("propNames", assetName, false);
             Vector3 doguPosition = new Vector3(0f, 0f, 0.5f);
             Vector3 doguScale = Vector3.one;
 
             if (assetName.EndsWith(".menu"))
             {
                 dogu = MenuFileUtility.LoadModel(assetName);
+                string handItem = Utility.HandItemToOdogu(assetName);
+                if (Translation.Has("propNames", handItem))
+                {
+                    doguName = Translation.Get("propNames", handItem);
+                }
             }
             else if (assetName.StartsWith("BG_"))
             {
@@ -128,6 +153,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                     dogu = GameObject.Instantiate(obj);
                     doguPosition = Vector3.zero;
                     doguScale = Vector3.one * 0.1f;
+                    doguName = Translation.Get("bgNames", "assetName");
                 }
 
             }
@@ -241,7 +267,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                 // TODO: Figure out why some props aren't centred properly
                 // Doesn't happen in MM but even after copy pasting the code, it doesn't work :/
                 GameObject deploymentObject = GetDeploymentObject();
-                GameObject finalDogu = new GameObject();
+                GameObject finalDogu = new GameObject(doguName);
 
                 dogu.transform.localScale = doguScale;
 
@@ -249,7 +275,6 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                 finalDogu.transform.SetParent(deploymentObject.transform, false);
 
                 finalDogu.transform.position = doguPosition;
-                finalDogu.name = doguName;
 
                 GameObject dragPoint = BaseDrag.MakeDragPoint(
                     PrimitiveType.Cube, Vector3.one * 0.12f, BaseDrag.LightBlue
@@ -257,10 +282,11 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
 
                 DragDogu dragDogu = dragPoint.AddComponent<DragDogu>();
                 dragDogu.Initialize(finalDogu);
-                dragDogu.Delete += (s, a) => DeleteDogu();
+                dragDogu.Delete += DeleteDogu;
                 dragDogu.SetDragProp(showGizmos, false, false);
                 doguList.Add(dragDogu);
                 dragDogu.DragPointScale = dragDogu.BaseScale * (CubeSmall ? 0.4f : 1f);
+                OnDoguListChange();
             }
             else
             {
@@ -268,7 +294,77 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             }
         }
 
-        private void DeleteDogu()
+        public DragDogu GetDogu(int doguIndex)
+        {
+            if (doguList.Count == 0 || doguIndex >= doguList.Count || doguIndex < 0) return null;
+            return doguList[doguIndex];
+        }
+
+        public void AttachProp(
+            int doguIndex, DragPointManager.AttachPoint attachPoint, Meido meido, bool worldPositionStays = true
+        )
+        {
+            if (doguList.Count == 0 || doguIndex >= doguList.Count || doguIndex < 0) return;
+            AttachProp(doguList[doguIndex], attachPoint, meido, worldPositionStays);
+        }
+
+        private void AttachProp(
+            DragDogu dragDogu, DragPointManager.AttachPoint attachPoint, Meido meido, bool worldPositionStays = true
+        )
+        {
+            GameObject dogu = dragDogu.Dogu;
+
+            Transform attachPointTransform = meido?.GetBoneTransform(attachPoint) ?? GetDeploymentObject().transform;
+
+            dragDogu.attachPointInfo = new DragPointManager.AttachPointInfo(
+                attachPoint: meido == null ? DragPointManager.AttachPoint.None : attachPoint,
+                maidGuid: meido == null ? String.Empty : meido.Maid.status.guid,
+                maidIndex: meido == null ? -1 : meido.ActiveSlot
+            );
+
+            worldPositionStays = meido == null ? true : worldPositionStays;
+
+            Vector3 position = dogu.transform.position;
+            Quaternion rotation = dogu.transform.rotation;
+
+            dogu.transform.SetParent(attachPointTransform, worldPositionStays);
+
+            if (worldPositionStays)
+            {
+                dogu.transform.position = position;
+                dogu.transform.rotation = rotation;
+            }
+            else
+            {
+                dogu.transform.localPosition = Vector3.zero;
+                dogu.transform.rotation = Quaternion.identity;
+            }
+
+            if (meido == null) Utility.FixGameObjectScale(dogu);
+        }
+
+        private void DetachProps(object sender, EventArgs args)
+        {
+            foreach (DragDogu dogu in doguList)
+            {
+                if (dogu.attachPointInfo.AttachPoint != DragPointManager.AttachPoint.None)
+                {
+                    dogu.Dogu.transform.SetParent(GetDeploymentObject().transform, true);
+                }
+            }
+        }
+
+        private void ReattachProps(object sender, EventArgs args)
+        {
+            foreach (DragDogu dragDogu in doguList)
+            {
+                Meido meido = this.meidoManager.GetMeido(dragDogu.attachPointInfo.MaidGuid);
+                bool worldPositionStays = meido == null;
+                AttachProp(dragDogu, dragDogu.attachPointInfo.AttachPoint, meido, worldPositionStays);
+            }
+        }
+
+        private void DeleteDogu(object sender, EventArgs args)
         {
             doguList.RemoveAll(dragDogu =>
                 {
@@ -280,6 +376,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                     return false;
                 }
             );
+            OnDoguListChange();
         }
 
         private void OnCubeSmall(object sender, EventArgs args)
@@ -289,5 +386,10 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                 dogu.DragPointScale = dogu.BaseScale * (CubeSmall ? 0.4f : 1f);
             }
         }
+
+        private void OnDoguListChange()
+        {
+            this.DoguListChange?.Invoke(this, EventArgs.Empty);
+        }
     }
 }

+ 5 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/Meido/Meido.cs

@@ -321,6 +321,11 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             body.FixVisibleFlag(false);
         }
 
+        public Transform GetBoneTransform(DragPointManager.AttachPoint point)
+        {
+            return this.dragPointManager?.GetAttachPointTransform(point);
+        }
+
         private void OnBodyLoad()
         {
             BodyLoad?.Invoke(this, EventArgs.Empty);

+ 2 - 2
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/MeidoPhotoStudio.cs

@@ -142,7 +142,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             initialized = true;
 
             meidoManager = new MeidoManager();
-            environmentManager = new EnvironmentManager();
+            environmentManager = new EnvironmentManager(meidoManager);
             messageWindowManager = new MessageWindowManager();
 
             MaidSwitcherPane maidSwitcherPane = new MaidSwitcherPane(meidoManager);
@@ -155,7 +155,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                     [Constants.Window.Pose] = new PoseWindowPane(meidoManager, maidSwitcherPane),
                     [Constants.Window.Face] = new FaceWindowPane(meidoManager, maidSwitcherPane),
                     [Constants.Window.BG] = new BGWindowPane(environmentManager),
-                    [Constants.Window.BG2] = new BG2WindowPane(environmentManager)
+                    [Constants.Window.BG2] = new BG2WindowPane(meidoManager, environmentManager)
                 },
                 [Constants.Window.Message] = new MessageWindow(messageWindowManager)
             };

+ 2 - 2
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/Translation.cs

@@ -95,9 +95,9 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             return true;
         }
 
-        public static string Get(string category, string text)
+        public static string Get(string category, string text, bool warn = true)
         {
-            return Has(category, text, true) ? Translations[category][text] : text;
+            return Has(category, text, warn) ? Translations[category][text] : text;
         }
 
         public static string[] GetArray(string category, IEnumerable<string> list)

+ 7 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/Utility.cs

@@ -103,5 +103,12 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             menu = $"odogu_{menu}";
             return menu;
         }
+
+        public static void FixGameObjectScale(GameObject go)
+        {
+            Vector3 scale = go.transform.localScale;
+            float largest = Mathf.Max(scale.x, Mathf.Max(scale.y, scale.z));
+            go.transform.localScale = Vector3.one * (float)Math.Round(largest, 3);
+        }
     }
 }