private void Init_SexLabAnimations()
        {
            string sexlab_dir = Game_Path + @"\FunScripts\SexLab";

            string[] mod_dirs = Directory.GetDirectories(sexlab_dir);

            foreach (string mod_dir in mod_dirs)
            {
                if (mod_dir.ToLower() == "o****m")
                {
                    SexLab_Orgasm_Event = new Actor_Data("o****m", mod_dir);
                    continue;
                }

                string[] animation_dirs = Directory.GetDirectories(mod_dir);
                foreach (string animation_dir in animation_dirs)
                {
                    string name = Path.GetFileName(animation_dir).ToLower();
                    SexLab_Animations.Add(new Animation_Data(name, animation_dir));
                }
            }


            int funscript_count = 0;

            foreach (Animation_Data sl_a in SexLab_Animations)
            {
                foreach (Stage_Data s_d in sl_a.stages)
                {
                    if (s_d == null)
                    {
                        continue;
                    }
                    foreach (Actor_Data p_d in s_d.positions)
                    {
                        if (p_d == null)
                        {
                            continue;
                        }
                        foreach (BodyPart_Data b_d in p_d.bodyparts)
                        {
                            if (b_d == null)
                            {
                                continue;
                            }
                            foreach (EventType_Data e_d in b_d.eventTypes)
                            {
                                funscript_count++;
                            }
                        }
                    }
                }
            }

            Notification_Message?.Invoke(this, new StringArg(String.Format("Registered {0} SexLab animations, with a total of {1} funscripts", SexLab_Animations.Count, funscript_count)));
        }
        public Stage_Data(String stage_dir)
        {
            String[] position_dirs = Directory.GetDirectories(stage_dir);

            foreach (String position_dir in position_dirs)
            {
                int index = Int32.Parse(Path.GetFileName(position_dir).Substring(1));
                positions[index - 1] = new Actor_Data(position_dir);
            }
        }
        //return the current playing sexlab event
        public void SexLab_Update_Event()
        {
            SexLab_Animation_Changed?.Invoke(this, new StringArg(String.Format("{0} S-{1}, P-{2}", Sexlab_Name, Sexlab_Stage, Sexlab_Position)));
            sexLab_running_Event.ForEach(runningEvent => runningEvent.End());
            Stage_Data stage_data = Sexlab_Playing_Animation.stages[Sexlab_Stage];

            if (stage_data != null)
            {
                Actor_Data position_data = stage_data.positions[Sexlab_Position];
                sexLab_running_Event = PlayEvent(position_data, true);
            }
        }
        private List <Running_Event> PlayEvent(Actor_Data event_data, bool synced_by_animation)
        {
            // Some events are meant for multiple body parts. If a physical device is mapped to more than one of these body parts AND
            // a single event triggers multiple body parts in one call of this method, only the first effect is played on a device.
            uint devicesReceivedEventAlready = 0;  // used as bitmask for Device.devices
            uint currentDeviceIndex;

            // These loops can create 0, 1 or more events on a device, e.g. if one device is registered for multiple body parts.
            // We return a list of all created events so we don't loose the reference to an event object,
            // because we need to be able to call the End() method to prematurely stop an event.

            List <Running_Event> runningEvents = new List <Running_Event>();

            foreach (BodyPart_Data bodypart in event_data.bodyparts)
            {
                if (bodypart == null)
                {
                    continue;
                }
                Device.BodyPart bodyPart_id = bodypart.bodyPart;  // Head, Body, Breast, ...

                foreach (EventType_Data eventType in bodypart.eventTypes)
                {
                    if (eventType == null)
                    {
                        continue;
                    }
                    Device.EventType eventType_id = eventType.eventType;  // Shock, Damage, Penetrate, Vibrate, Equip (with footsteps==Penetrate)

                    currentDeviceIndex = 1;
                    foreach (Device device in Device.devices)                                                                     // physical device
                    {
                        if (device.HasType(bodyPart_id, eventType_id) && (devicesReceivedEventAlready & currentDeviceIndex) == 0) // and device did not already receive an event in this method's call
                        {
                            // if (event_data.name.StartsWith("dd vibrator"))  // Debug
                            // {
                            //     Notification_Message?.Invoke(this, new StringArg(String.Format(
                            //         "Creating event {0} at part {1} with type {2}", event_data.name, bodyPart_id.ToString(), eventType_id.ToString())
                            //     ));
                            // }
                            runningEvents.Add(device.AddEvent(event_data.name, eventType.actions, synced_by_animation));
                            devicesReceivedEventAlready = devicesReceivedEventAlready | currentDeviceIndex;
                        }
                        currentDeviceIndex = currentDeviceIndex << 1;
                    }
                }
            }
            return(runningEvents);
        }
 private List <Running_Event> PlayEvent(Actor_Data event_data)
 {
     return(PlayEvent(event_data, false));
 }