public static int ImportLegacyData(this LetterAnimation letter_anim, List <object> data_list, int index_offset = 0)
        {
            KeyValuePair <string, string> value_pair;
            string key, value;
            int    idx;
            int    loop_idx = 0, action_idx = 0;

            for (idx = index_offset; idx < data_list.Count; idx++)
            {
                value_pair = (KeyValuePair <string, string>)data_list[idx];
                key        = value_pair.Key;
                value      = value_pair.Value;

                if (key.Equals("ANIM_DATA_END"))
                {
                    // reached end of this animations import data
                    break;
                }

                switch (key)
                {
                case "m_letters_to_animate":
                    List <object> letter_list = value.StringToList(';');
                    letter_anim.m_letters_to_animate = new List <int>();
                    if (letter_list != null)
                    {
                        foreach (object obj in letter_list)
                        {
                            letter_anim.m_letters_to_animate.Add(int.Parse(obj.ToString()));
                        }
                    }
                    break;

                case "m_letters_to_animate_custom_idx":
                    letter_anim.m_letters_to_animate_custom_idx = int.Parse(value); break;

                case "m_letters_to_animate_option":
                    letter_anim.m_letters_to_animate_option = (LETTERS_TO_ANIMATE)int.Parse(value); break;


                // LOOP DATA IMPORT
                case "LOOP_DATA_START":
                    if (loop_idx == letter_anim.NumLoops)
                    {
                        letter_anim.AddLoop();
                    }
                    break;

                case "LOOP_DATA_END":
                    loop_idx++; break;

                case "m_delay_first_only":
                    letter_anim.GetLoop(loop_idx).m_delay_first_only = bool.Parse(value); break;

                case "m_end_action_idx":
                    letter_anim.GetLoop(loop_idx).m_end_action_idx = int.Parse(value); break;

                case "m_loop_type":
                    letter_anim.GetLoop(loop_idx).m_loop_type = (LOOP_TYPE)int.Parse(value); break;

                case "m_number_of_loops":
                    letter_anim.GetLoop(loop_idx).m_number_of_loops = int.Parse(value); break;

                case "m_start_action_idx":
                    letter_anim.GetLoop(loop_idx).m_start_action_idx = int.Parse(value); break;


                // ACTION DATA IMPORT
                case "ACTION_DATA_START":
                    if (action_idx == letter_anim.NumActions)
                    {
                        letter_anim.AddAction();
                    }
                    idx = letter_anim.GetAction(action_idx).ImportLegacyData(data_list, idx + 1);
                    action_idx++;
                    break;
                }
            }

            // Remove any extra LoopData or LetterAction instances that existed prior to importing
            if (letter_anim.NumLoops > loop_idx)
            {
                letter_anim.RemoveLoops(loop_idx, letter_anim.NumLoops - loop_idx);
            }

            if (letter_anim.NumActions > action_idx)
            {
                letter_anim.RemoveActions(action_idx, letter_anim.NumActions - action_idx);
            }

            return(idx);
        }
		public void CalculatePositionProgressions(TextFxAnimationManager anim_manager,
		                                          LetterAnimation letter_animation,
		                                          ref float[] letter_progressions,
		                                          LetterSetup[] letters,
		                                          int num_progressions,
		                                          ActionPositionVector3Progression offset_prog,
		                                          bool variableActive = true)
		{

//			if (Progression == CURVE_OPTION_INDEX) {
//				if(m_is_offset_from_last)
//				{
//					m_value_state = PROGRESSION_VALUE_STATE.OFFSET_FROM_REFERENCE;
//					m_offset_progression = offset_prog.GetOffsetReference();
//				}
//				else
//					m_value_state = PROGRESSION_VALUE_STATE.UNIQUE;
//
//				m_values = new Vector3[num_progressions];
//
//				float baseYOffset = letters[0].BaseVerticesCenter.y - letters[0].BaseVerticesBL.y;
//				Vector3 firstLetterCenter = letters[0].BaseVerticesCenter;
//
//				for(int idx=0; idx < letter_progressions.Length; idx++)
//				{
//					m_values[idx] += m_bezier_curve.GetCurvePoint(letter_progressions[idx], yOffset : baseYOffset + (letters[idx].BaseVerticesCenter.y - firstLetterCenter.y));
//				}
//			}
//			else
//			{
				CalculateProgressions(num_progressions, offset_prog, variableActive);
//			}

			if(Progression == CURVE_OPTION_INDEX || (m_value_state == PROGRESSION_VALUE_STATE.REFERENCE ? GetOffsetReference().GetPositionVector3Prog(letter_animation.LetterActions).m_force_position_override : m_force_position_override))
			{
				Vector3 base_offset = Vector3.zero;

				if(m_values.Length == 1)
				{
					base_offset = m_values[0];
					m_values = new Vector3[letters.Length];
				}

				// Remove the base positioning offset for each letter
				for(int letter_idx =0; letter_idx < letters.Length; letter_idx++)
				{
					m_values[letter_idx] += base_offset;
					m_values[letter_idx] -= letters[letter_idx].BaseVerticesCenter / anim_manager.AnimationInterface.MovementScale;
				}
			}
		}
		public void PrepareData(TextFxAnimationManager anim_manager,
								ref LetterSetup[] letters,
		                        LetterAnimation animation_ref,
		                        int action_idx,
		                        ANIMATION_DATA_TYPE what_to_update,
		                        int num_letters,
		                        int num_white_space_chars_to_include,
		                        int num_words,
		                        int num_lines,
		                        LetterAction prev_action,
		                        AnimatePerOptions animate_per,
		                        ActionColorProgression defaultTextColour,
		                        bool prev_action_end_state = true)
		{
			// Set progression reference datas 
			m_start_colour.SetReferenceData(action_idx, ANIMATION_DATA_TYPE.COLOUR, true);
			m_start_euler_rotation.SetReferenceData(action_idx, ANIMATION_DATA_TYPE.LOCAL_ROTATION, true);
			m_start_pos.SetReferenceData(action_idx, ANIMATION_DATA_TYPE.POSITION, true);
			m_start_scale.SetReferenceData(action_idx, ANIMATION_DATA_TYPE.LOCAL_SCALE, true);
			m_global_start_euler_rotation.SetReferenceData(action_idx, ANIMATION_DATA_TYPE.GLOBAL_ROTATION, true);
			m_global_start_scale.SetReferenceData(action_idx, ANIMATION_DATA_TYPE.GLOBAL_SCALE, true);
			m_end_colour.SetReferenceData(action_idx, ANIMATION_DATA_TYPE.COLOUR, false);
			m_end_euler_rotation.SetReferenceData(action_idx, ANIMATION_DATA_TYPE.LOCAL_ROTATION, false);
			m_end_pos.SetReferenceData(action_idx, ANIMATION_DATA_TYPE.POSITION, false);
			m_end_scale.SetReferenceData(action_idx, ANIMATION_DATA_TYPE.LOCAL_SCALE, false);
			m_global_end_euler_rotation.SetReferenceData(action_idx, ANIMATION_DATA_TYPE.GLOBAL_ROTATION, false);
			m_global_end_scale.SetReferenceData(action_idx, ANIMATION_DATA_TYPE.GLOBAL_SCALE, false);


			// Prepare Data

			if(what_to_update == ANIMATION_DATA_TYPE.DURATION || what_to_update == ANIMATION_DATA_TYPE.ALL)
				m_duration_progression.CalculateProgressions(GetProgressionTotal(num_letters, num_words, num_lines, animate_per, m_duration_progression.AnimatePer, m_duration_progression.OverrideAnimatePerOption));


			if((what_to_update == ANIMATION_DATA_TYPE.AUDIO_EFFECTS || what_to_update == ANIMATION_DATA_TYPE.ALL) && m_audio_effects != null)
			{
				foreach(AudioEffectSetup effect_setup in m_audio_effects)
				{
					effect_setup.m_delay.CalculateProgressions(GetProgressionTotal(num_letters, num_words, num_lines, animate_per, effect_setup.m_delay.AnimatePer, effect_setup.m_delay.OverrideAnimatePerOption));
					effect_setup.m_offset_time.CalculateProgressions(GetProgressionTotal(num_letters, num_words, num_lines, animate_per, effect_setup.m_offset_time.AnimatePer, effect_setup.m_offset_time.OverrideAnimatePerOption));
					effect_setup.m_volume.CalculateProgressions(GetProgressionTotal(num_letters, num_words, num_lines, animate_per, effect_setup.m_volume.AnimatePer, effect_setup.m_volume.OverrideAnimatePerOption));
					effect_setup.m_pitch.CalculateProgressions(GetProgressionTotal(num_letters, num_words, num_lines, animate_per, effect_setup.m_pitch.AnimatePer, effect_setup.m_pitch.OverrideAnimatePerOption));
				}
			}

			if((what_to_update == ANIMATION_DATA_TYPE.PARTICLE_EFFECTS || what_to_update == ANIMATION_DATA_TYPE.ALL) && m_particle_effects != null)
			{
				foreach(ParticleEffectSetup effect_setup in m_particle_effects)
				{
					effect_setup.m_position_offset.CalculateProgressions(GetProgressionTotal(num_letters, num_words, num_lines, animate_per, effect_setup.m_position_offset.AnimatePer, effect_setup.m_position_offset.OverrideAnimatePerOption), null);
					effect_setup.m_rotation_offset.CalculateProgressions(GetProgressionTotal(num_letters, num_words, num_lines, animate_per, effect_setup.m_rotation_offset.AnimatePer, effect_setup.m_rotation_offset.OverrideAnimatePerOption), null);
					effect_setup.m_delay.CalculateProgressions(GetProgressionTotal(num_letters, num_words, num_lines, animate_per, effect_setup.m_delay.AnimatePer, effect_setup.m_delay.OverrideAnimatePerOption));
					effect_setup.m_duration.CalculateProgressions(GetProgressionTotal(num_letters, num_words, num_lines, animate_per, effect_setup.m_duration.AnimatePer, effect_setup.m_duration.OverrideAnimatePerOption));
				}
			}
			
			if(m_action_type == ACTION_TYPE.BREAK)
			{
				if(prev_action != null)
				{
					m_start_colour.SetValueReference(prev_action_end_state ? prev_action.m_end_colour : prev_action.m_start_colour);
					m_start_euler_rotation.SetValueReference( prev_action_end_state ? prev_action.m_end_euler_rotation : prev_action.m_start_euler_rotation );
					m_start_pos.SetValueReference( prev_action_end_state ? prev_action.m_end_pos : prev_action.m_start_pos );
					m_start_scale.SetValueReference( prev_action_end_state ? prev_action.m_end_scale : prev_action.m_start_scale );
					m_global_start_euler_rotation.SetValueReference( prev_action_end_state ? prev_action.m_global_end_euler_rotation : prev_action.m_global_start_euler_rotation );
					m_global_start_scale.SetValueReference( prev_action_end_state ? prev_action.m_global_end_scale : prev_action.m_global_start_scale );
				}

				m_end_colour.SetValueReference(m_start_colour);
				m_end_euler_rotation.SetValueReference(m_start_euler_rotation );
				m_end_pos.SetValueReference( prev_action.m_start_pos );
				m_end_scale.SetValueReference( prev_action.m_start_scale );
				m_global_end_euler_rotation.SetValueReference(prev_action.m_global_start_euler_rotation );
				m_global_end_scale.SetValueReference( prev_action.m_global_start_scale );

				return;
			}

			if (animation_ref.m_letters_to_animate_option != LETTERS_TO_ANIMATE.ALL_LETTERS || (((ValueProgression)m_delay_progression.Progression) != ValueProgression.Eased && ((ValueProgression)m_delay_progression.Progression) != ValueProgression.EasedCustom))
				m_delay_with_white_space_influence = false;


			if(what_to_update == ANIMATION_DATA_TYPE.DELAY || what_to_update == ANIMATION_DATA_TYPE.ALL)
				m_delay_progression.CalculateProgressions(GetProgressionTotal(num_letters + (m_delay_with_white_space_influence ? num_white_space_chars_to_include : 0), num_words, num_lines, animate_per, m_delay_progression.AnimatePer, m_delay_progression.OverrideAnimatePerOption));
			

			if(what_to_update == ANIMATION_DATA_TYPE.COLOUR || what_to_update == ANIMATION_DATA_TYPE.ALL)
			{
				if(m_offset_from_last && prev_action != null)
				{
					m_start_colour.SetValueReference(prev_action_end_state ? prev_action.m_end_colour : prev_action.m_start_colour );
				}
				else
				{
					m_start_colour.CalculateProgressions(GetProgressionTotal(num_letters, num_words, num_lines, animate_per, m_start_colour.AnimatePer, m_start_colour.OverrideAnimatePerOption), 
					                                     prev_action != null ? prev_action.m_end_colour : defaultTextColour,
					                                     prev_action == null || m_colour_transition_active);
				}

				m_end_colour.CalculateProgressions(GetProgressionTotal(num_letters, num_words, num_lines, animate_per, m_end_colour.AnimatePer, m_end_colour.OverrideAnimatePerOption),
				                                   m_start_colour,
				                                   prev_action == null || m_colour_transition_active);
			}
			
			
			if(m_offset_from_last && prev_action != null)
			{
				if(what_to_update == ANIMATION_DATA_TYPE.POSITION || what_to_update == ANIMATION_DATA_TYPE.ALL)
					m_start_pos.SetValueReference( prev_action_end_state ? prev_action.m_end_pos : prev_action.m_start_pos );

				if(what_to_update == ANIMATION_DATA_TYPE.LOCAL_ROTATION || what_to_update == ANIMATION_DATA_TYPE.ALL)
					m_start_euler_rotation.SetValueReference( prev_action_end_state ? prev_action.m_end_euler_rotation : prev_action.m_start_euler_rotation );

				if(what_to_update == ANIMATION_DATA_TYPE.LOCAL_SCALE || what_to_update == ANIMATION_DATA_TYPE.ALL)
					m_start_scale.SetValueReference( prev_action_end_state ? prev_action.m_end_scale : prev_action.m_start_scale );

				if(what_to_update == ANIMATION_DATA_TYPE.GLOBAL_ROTATION || what_to_update == ANIMATION_DATA_TYPE.ALL)
					m_global_start_euler_rotation.SetValueReference( prev_action_end_state ? prev_action.m_global_end_euler_rotation : prev_action.m_global_start_euler_rotation );

				if(what_to_update == ANIMATION_DATA_TYPE.GLOBAL_SCALE || what_to_update == ANIMATION_DATA_TYPE.ALL)
					m_global_start_scale.SetValueReference( prev_action_end_state ? prev_action.m_global_end_scale : prev_action.m_global_start_scale );
			}
			else
			{
				float[] start_pos_curve_letter_progressions = null;
				if(	(	what_to_update == ANIMATION_DATA_TYPE.POSITION ||
					    what_to_update == ANIMATION_DATA_TYPE.LOCAL_ROTATION ||
				     	what_to_update == ANIMATION_DATA_TYPE.GLOBAL_ROTATION ||
				     	what_to_update == ANIMATION_DATA_TYPE.ALL
				    )
					&& m_start_pos.Progression == ActionPositionVector3Progression.CURVE_OPTION_INDEX)
				{
					// Pre calculate letter progression values based on letter spacing
					start_pos_curve_letter_progressions = m_start_pos.BezierCurve.GetLetterProgressions(anim_manager, ref letters, m_letter_anchor_start);
				}

				if(what_to_update == ANIMATION_DATA_TYPE.POSITION || what_to_update == ANIMATION_DATA_TYPE.ALL)
					m_start_pos.CalculatePositionProgressions(	anim_manager,
					                                          	animation_ref,
					                                          	ref start_pos_curve_letter_progressions,
					                                          	letters,
					                                          	GetProgressionTotal(num_letters + num_white_space_chars_to_include, num_words, num_lines, animate_per, m_start_pos.AnimatePer, m_start_pos.OverrideAnimatePerOption),
																prev_action != null ? prev_action.m_end_pos : null,
					                                          	prev_action == null || m_position_transition_active);

				if(what_to_update == ANIMATION_DATA_TYPE.LOCAL_ROTATION || what_to_update == ANIMATION_DATA_TYPE.ALL)
					m_start_euler_rotation.CalculateRotationProgressions(ref start_pos_curve_letter_progressions, GetProgressionTotal(num_letters + num_white_space_chars_to_include, num_words, num_lines, animate_per, m_start_euler_rotation.AnimatePer, m_start_euler_rotation.OverrideAnimatePerOption),
																			prev_action != null ? prev_action.m_end_euler_rotation : null,
			                                                     			m_start_pos.Progression == ActionPositionVector3Progression.CURVE_OPTION_INDEX ? m_start_pos.BezierCurve : null,
					                                                     	prev_action == null || m_local_rotation_transition_active);

				if(what_to_update == ANIMATION_DATA_TYPE.LOCAL_SCALE || what_to_update == ANIMATION_DATA_TYPE.ALL)
					m_start_scale.CalculateProgressions(GetProgressionTotal(num_letters + num_white_space_chars_to_include, num_words, num_lines, animate_per, m_start_scale.AnimatePer, m_start_scale.OverrideAnimatePerOption),
														prev_action != null ? prev_action.m_end_scale : null,
					                                    prev_action == null || m_local_scale_transition_active);

				if(what_to_update == ANIMATION_DATA_TYPE.GLOBAL_ROTATION || what_to_update == ANIMATION_DATA_TYPE.ALL)
					m_global_start_euler_rotation.CalculateRotationProgressions(ref start_pos_curve_letter_progressions, GetProgressionTotal(num_letters + num_white_space_chars_to_include, num_words, num_lines, animate_per, m_global_start_euler_rotation.AnimatePer, m_global_start_euler_rotation.OverrideAnimatePerOption),
					                                                            prev_action != null ? prev_action.m_global_end_euler_rotation : null,
					                                                            null,
					                                                            prev_action == null || m_global_rotation_transition_active);

				if(what_to_update == ANIMATION_DATA_TYPE.GLOBAL_SCALE || what_to_update == ANIMATION_DATA_TYPE.ALL)
					m_global_start_scale.CalculateProgressions(	GetProgressionTotal(num_letters + num_white_space_chars_to_include, num_words, num_lines, animate_per, m_global_start_scale.AnimatePer, m_global_start_scale.OverrideAnimatePerOption),
				                                           		prev_action != null ? prev_action.m_global_end_scale : null,
					                                           	prev_action == null || m_global_scale_transition_active);
			}
			
			float[] end_pos_curve_letter_progressions = null;
			if(	(	what_to_update == ANIMATION_DATA_TYPE.POSITION ||
			     	what_to_update == ANIMATION_DATA_TYPE.LOCAL_ROTATION ||
			     	what_to_update == ANIMATION_DATA_TYPE.GLOBAL_ROTATION ||
			     	what_to_update == ANIMATION_DATA_TYPE.ALL
			    )
			   	&& m_end_pos.Progression == ActionPositionVector3Progression.CURVE_OPTION_INDEX)
			{
				// Pre calculate letter progression values based on letter spacing
				end_pos_curve_letter_progressions = m_end_pos.BezierCurve.GetLetterProgressions(anim_manager, ref letters, m_letter_anchor_end);
			}

			if(what_to_update == ANIMATION_DATA_TYPE.POSITION || what_to_update == ANIMATION_DATA_TYPE.ALL)
				m_end_pos.CalculatePositionProgressions(anim_manager,
				                                        animation_ref,
				                                        ref end_pos_curve_letter_progressions,
				                                        letters,
				                                        GetProgressionTotal(num_letters + num_white_space_chars_to_include, num_words, num_lines, animate_per, m_end_pos.AnimatePer, m_end_pos.OverrideAnimatePerOption),
				                                        m_start_pos,
				                                        prev_action == null || m_position_transition_active);

			if(what_to_update == ANIMATION_DATA_TYPE.LOCAL_ROTATION || what_to_update == ANIMATION_DATA_TYPE.ALL)
				m_end_euler_rotation.CalculateRotationProgressions(ref end_pos_curve_letter_progressions, GetProgressionTotal(num_letters + num_white_space_chars_to_include, num_words, num_lines, animate_per, m_end_euler_rotation.AnimatePer, m_end_euler_rotation.OverrideAnimatePerOption),
				                                                   m_start_euler_rotation,
				                                                   m_end_pos.Progression == ActionPositionVector3Progression.CURVE_OPTION_INDEX ? m_end_pos.BezierCurve : null,
				                                                   prev_action == null || m_local_rotation_transition_active);

			if(what_to_update == ANIMATION_DATA_TYPE.LOCAL_SCALE || what_to_update == ANIMATION_DATA_TYPE.ALL)
				m_end_scale.CalculateProgressions(GetProgressionTotal(num_letters + num_white_space_chars_to_include, num_words, num_lines, animate_per, m_end_scale.AnimatePer, m_end_scale.OverrideAnimatePerOption),
				                                  m_start_scale,
				                                  prev_action == null || m_local_scale_transition_active);

			if(what_to_update == ANIMATION_DATA_TYPE.GLOBAL_ROTATION || what_to_update == ANIMATION_DATA_TYPE.ALL)
				m_global_end_euler_rotation.CalculateRotationProgressions(ref end_pos_curve_letter_progressions, GetProgressionTotal(num_letters + num_white_space_chars_to_include, num_words, num_lines, animate_per, m_global_end_euler_rotation.AnimatePer, m_global_end_euler_rotation.OverrideAnimatePerOption),
				                                                          m_global_start_euler_rotation,
				                                                          null,
				                                                          prev_action == null || m_global_rotation_transition_active);

			if(what_to_update == ANIMATION_DATA_TYPE.GLOBAL_SCALE || what_to_update == ANIMATION_DATA_TYPE.ALL)
				m_global_end_scale.CalculateProgressions(GetProgressionTotal(num_letters + num_white_space_chars_to_include, num_words, num_lines, animate_per, m_global_end_scale.AnimatePer, m_global_end_scale.OverrideAnimatePerOption),
				                                         m_global_start_scale,
				                                         prev_action == null || m_global_scale_transition_active);

			if(what_to_update == ANIMATION_DATA_TYPE.POSITION
			   || what_to_update == ANIMATION_DATA_TYPE.POSITION
			   || what_to_update == ANIMATION_DATA_TYPE.LETTER_ANCHOR
			   || what_to_update == ANIMATION_DATA_TYPE.ALL)
				CalculateLetterAnchorOffset();
		}
		public void ImportData(string data, bool force_clear_old_audio_particles = false)
		{
			if(force_clear_old_audio_particles)
				ClearCachedAudioParticleInstances(true);
			
			Boomlagoon.JSON.JSONObject json_data = Boomlagoon.JSON.JSONObject.Parse(data, true);
			
			if(json_data != null)
			{
				m_animate_per = (AnimatePerOptions) (int) json_data["m_animate_per"].Number;
				
				if(json_data.ContainsKey("m_begin_delay")) m_begin_delay = (float) json_data["m_begin_delay"].Number;
				if(json_data.ContainsKey("m_begin_on_start")) m_begin_on_start = json_data["m_begin_on_start"].Boolean;
				if(json_data.ContainsKey("m_on_finish_action")) m_on_finish_action = (ON_FINISH_ACTION) (int) json_data["m_on_finish_action"].Number;
				if(json_data.ContainsKey("m_time_type")) m_time_type = (AnimationTime) (int) json_data["m_time_type"].Number;
				
				m_master_animations = new List<LetterAnimation>();
				LetterAnimation letter_anim;
				foreach(Boomlagoon.JSON.JSONValue animation_data in json_data["LETTER_ANIMATIONS_DATA"].Array)
				{
					letter_anim = new LetterAnimation();
					letter_anim.ImportData(animation_data.Obj, m_animation_interface_reference.AssetNameSuffix);
					m_master_animations.Add(letter_anim);
				}

#if UNITY_EDITOR
				m_preset_effect_settings = new List<PresetEffectSetting>();

				// Import any Quick setup settings info
				if(json_data.ContainsKey("PRESET_EFFECT_SETTINGS"))
				{
					PresetEffectSetting effectSetting;
					foreach(Boomlagoon.JSON.JSONValue effectSettingData in json_data["PRESET_EFFECT_SETTINGS"].Array)
					{
						effectSetting = new PresetEffectSetting();
						effectSetting.ImportData(effectSettingData.Obj);
						m_preset_effect_settings.Add(effectSetting);
					}
				}
#endif
			}
			else
			{
				// Import string is not valid JSON, therefore assuming it is in the legacy data import format.
				Debug.LogError("TextFx animation import failed. Non-valid JSON data provided");

				this.ImportLegacyData(data);

#if UNITY_EDITOR
				m_preset_effect_settings = new List<PresetEffectSetting>();
#endif
			}


			if(!Application.isPlaying && m_animation_interface_reference.Text.Equals(""))
				m_animation_interface_reference.SetText("TextFx");

			PrepareAnimationData ();

			ResetAnimation();

			// Update mesh
			m_animation_interface_reference.UpdateTextFxMesh(MeshVerts, MeshColours);

#if UNITY_EDITOR
			SceneView.RepaintAll();
#endif
		}
		// Used for importing a preset animation section's data, to have its animation edited in the editor, or have its quick-edit settings edited
		public void ImportPresetAnimationSectionData(string data, bool force_clear_old_audio_particles = false)
		{
			if(force_clear_old_audio_particles)
				ClearCachedAudioParticleInstances(true);
			
			Boomlagoon.JSON.JSONObject json_data = Boomlagoon.JSON.JSONObject.Parse(data, true);
			
			if(json_data != null)
			{
				m_master_animations = new List<LetterAnimation>();
				LetterAnimation letter_anim = new LetterAnimation();
				letter_anim.ImportPresetSectionData(json_data["LETTER_ANIMATIONS_DATA"].Obj, m_letters, m_animation_interface_reference.AssetNameSuffix);

				m_master_animations.Add(letter_anim);
				
				m_preset_effect_settings = new List<PresetEffectSetting>();
				
				// Import any Quick setup settings info
				if(json_data.ContainsKey("PRESET_EFFECT_SETTINGS"))
				{
					PresetEffectSetting effectSetting;
					foreach(Boomlagoon.JSON.JSONValue effectSettingData in json_data["PRESET_EFFECT_SETTINGS"].Array)
					{
						effectSetting = new PresetEffectSetting();
						effectSetting.ImportData(effectSettingData.Obj);
						m_preset_effect_settings.Add(effectSetting);
					}
				}
			}
			else
			{
				// Import string is not valid JSON, therefore assuming it is in the legacy data import format.
				Debug.LogError("TextFx animation import failed. Non-valid JSON data provided");
			}
			
			if(!Application.isPlaying && m_animation_interface_reference.Text.Equals(""))
				m_animation_interface_reference.SetText("TextFx");
			
			PrepareAnimationData ();
			
			ResetAnimation();
			
			// Update mesh
			m_animation_interface_reference.UpdateTextFxMesh(MeshVerts, MeshColours);
			
			SceneView.RepaintAll();
		}
        public bool DrawGUISetting(TextFxAnimationManager animation_manager, float gui_x_offset, ref float gui_y_offset, bool gui_already_changed, int action_start_offset = 0, int loop_start_offset = 0)
        {
            LetterAnimation letterAnimation = animation_manager.GetAnimation(m_animation_idx);
            LetterAction    letterAction    = letterAnimation != null?letterAnimation.GetAction(m_action_idx + action_start_offset) : null;

            if (letterAction == null)
            {
                return(false);
            }


            if ((m_data_type == ANIMATION_DATA_TYPE.POSITION && m_startState && letterAction.m_start_pos.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.POSITION && !m_startState && letterAction.m_end_pos.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.LOCAL_ROTATION && m_startState && letterAction.m_start_euler_rotation.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.LOCAL_ROTATION && !m_startState && letterAction.m_end_euler_rotation.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.GLOBAL_ROTATION && m_startState && letterAction.m_global_start_euler_rotation.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.GLOBAL_ROTATION && !m_startState && letterAction.m_global_end_euler_rotation.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.LOCAL_SCALE && m_startState && letterAction.m_start_scale.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.LOCAL_SCALE && !m_startState && letterAction.m_end_scale.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.GLOBAL_SCALE && m_startState && letterAction.m_global_start_scale.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.GLOBAL_SCALE && !m_startState && letterAction.m_global_end_scale.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.COLOUR && m_startState && letterAction.m_start_colour.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
                (m_data_type == ANIMATION_DATA_TYPE.COLOUR && !m_startState && letterAction.m_end_colour.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)))
            {
                animation_manager.PrepareAnimationData(m_data_type);

                if (!animation_manager.Playing)
                {
                    // Set the current state of the animation to show effect of changes
                    animation_manager.SetAnimationState(m_action_idx,
                                                        m_startState ? 0 : 1,
                                                        update_mesh: true);
                }
            }
            else if (m_data_type == ANIMATION_DATA_TYPE.DURATION && letterAction.m_duration_progression.DrawQuickEditorGUI("Lerp Duration", gui_x_offset, ref gui_y_offset, gui_already_changed))
            {
                animation_manager.PrepareAnimationData(m_data_type);

                animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.DELAY);
            }
            else if (m_data_type == ANIMATION_DATA_TYPE.DELAY && letterAction.m_delay_progression.DrawQuickEditorGUI("Delay Easing (Seconds)", gui_x_offset, ref gui_y_offset, gui_already_changed))
            {
                animation_manager.PrepareAnimationData(m_data_type);
            }
//			else if(m_data_type == ANIMATION_DATA_TYPE.COLOUR_START_END && letterAction.m_start_colour.DrawQuickEditorGUI(this, ref gui_y_offset, gui_already_changed))
//			{
//				// Set end colour to be the same at start colour
//				if(letterAction.m_start_colour.Progression == (int) ValueProgression.Constant)
//					letterAction.m_end_colour.SetConstant(letterAction.m_start_colour.ValueFrom);
//				else if(letterAction.m_start_colour.Progression == (int) ValueProgression.Eased)
//				{
//					if(letterAction.m_start_colour.UsingThirdValue)
//						letterAction.m_end_colour.SetEased(letterAction.m_start_colour.ValueFrom, letterAction.m_start_colour.ValueTo, letterAction.m_start_colour.ValueThen);
//					else
//						letterAction.m_end_colour.SetEased(letterAction.m_start_colour.ValueFrom, letterAction.m_start_colour.ValueTo);
//				}
//				else if(letterAction.m_start_colour.Progression == (int) ValueProgression.EasedCustom)
//					letterAction.m_end_colour.SetEasedCustom(letterAction.m_start_colour.ValueFrom, letterAction.m_start_colour.ValueTo);
//				else if(letterAction.m_start_colour.Progression == (int) ValueProgression.Random)
//					letterAction.m_end_colour.SetRandom(letterAction.m_start_colour.ValueFrom, letterAction.m_start_colour.ValueTo);
//
//				animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.COLOUR_END);
//
//				animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.COLOUR_START);
//
//				if(!animation_manager.Playing)
//				{
//					// Set the current state of the animation to show effect of changes
//					animation_manager.SetAnimationState(m_action_idx, Mathf.Clamp(m_action_progress_state_override, 0f, 1f), update_mesh: true);
//				}
//			}
            else if (m_data_type == ANIMATION_DATA_TYPE.EASE_TYPE)
            {
                letterAction.m_ease_type = (EasingEquation)EditorGUI.EnumPopup(new Rect(gui_x_offset, gui_y_offset, 350, LINE_HEIGHT), m_setting_name, letterAction.m_ease_type);

                if (!gui_already_changed && GUI.changed)
                {
                    animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.ALL);
                }

                gui_y_offset += LINE_HEIGHT;
            }
            else if (m_data_type == ANIMATION_DATA_TYPE.DELAY_EASED_RANDOM_SWITCH)
            {
                bool newSelection = EditorGUI.Toggle(new Rect(gui_x_offset, gui_y_offset, 250, LINE_HEIGHT), "Randomised?", letterAction.m_delay_progression.Progression == (int)ValueProgression.Random);

                if (!gui_already_changed && GUI.changed)
                {
                    if (newSelection)
                    {
                        letterAction.m_delay_progression.SetRandom(letterAction.m_delay_progression.ValueFrom, letterAction.m_delay_progression.ValueTo, letterAction.m_delay_progression.UniqueRandomRaw);
                    }
                    else
                    {
                        letterAction.m_delay_progression.SetEased(letterAction.m_delay_progression.ValueFrom, letterAction.m_delay_progression.ValueTo);
                    }

                    animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.DELAY);
                }

                gui_y_offset += LINE_HEIGHT;
            }
            else if (m_data_type == ANIMATION_DATA_TYPE.NUM_LOOP_ITERATIONS)
            {
                ActionLoopCycle loop_cycle = letterAnimation.GetLoop(m_action_idx + loop_start_offset);

                if (loop_cycle != null)
                {
                    loop_cycle.m_number_of_loops = EditorGUI.IntField(new Rect(gui_x_offset, gui_y_offset, 250, LINE_HEIGHT), m_setting_name, loop_cycle.m_number_of_loops);
                }


                gui_y_offset += LINE_HEIGHT;
            }

            return(!gui_already_changed && GUI.changed);
        }
Beispiel #7
0
        public List <VariableStateListener> GetSettingData(TextFxAnimationManager animation_manager, int action_start_offset = 0, int loop_start_offset = 0)
        {
            LetterAnimation letterAnimation = animation_manager.GetAnimation(m_animation_idx);
            LetterAction    letterAction    = letterAnimation != null?letterAnimation.GetAction(m_action_idx + action_start_offset) : null;

            if (letterAction == null)
            {
                return(null);
            }


//			if((m_data_type == ANIMATION_DATA_TYPE.POSITION && m_startState && letterAction.m_start_pos.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
//			   (m_data_type == ANIMATION_DATA_TYPE.POSITION && !m_startState && letterAction.m_end_pos.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
//			   (m_data_type == ANIMATION_DATA_TYPE.LOCAL_ROTATION && m_startState && letterAction.m_start_euler_rotation.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
//			   (m_data_type == ANIMATION_DATA_TYPE.LOCAL_ROTATION && !m_startState && letterAction.m_end_euler_rotation.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
//			   (m_data_type == ANIMATION_DATA_TYPE.GLOBAL_ROTATION && m_startState && letterAction.m_global_start_euler_rotation.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
//			   (m_data_type == ANIMATION_DATA_TYPE.GLOBAL_ROTATION && !m_startState && letterAction.m_global_end_euler_rotation.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
//			   (m_data_type == ANIMATION_DATA_TYPE.LOCAL_SCALE && m_startState && letterAction.m_start_scale.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
//			   (m_data_type == ANIMATION_DATA_TYPE.LOCAL_SCALE && !m_startState && letterAction.m_end_scale.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
//			   (m_data_type == ANIMATION_DATA_TYPE.GLOBAL_SCALE && m_startState && letterAction.m_global_start_scale.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
//			   (m_data_type == ANIMATION_DATA_TYPE.GLOBAL_SCALE && !m_startState && letterAction.m_global_end_scale.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
//			   (m_data_type == ANIMATION_DATA_TYPE.COLOUR && m_startState && letterAction.m_start_colour.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)) ||
//			   (m_data_type == ANIMATION_DATA_TYPE.COLOUR && !m_startState && letterAction.m_end_colour.DrawQuickEditorGUI(this, gui_x_offset, ref gui_y_offset, gui_already_changed)))
//			{
//				animation_manager.PrepareAnimationData(m_data_type);
//
//				if(!animation_manager.Playing)
//				{
//					// Set the current state of the animation to show effect of changes
//					animation_manager.SetAnimationState(m_action_idx,
//					                                    m_startState ? 0 : 1,
//					                                    update_mesh: true);
//				}
//			}
//			else if(m_data_type == ANIMATION_DATA_TYPE.DURATION && letterAction.m_duration_progression.DrawQuickEditorGUI("Lerp Duration", gui_x_offset, ref gui_y_offset, gui_already_changed))
//			{
//				animation_manager.PrepareAnimationData(m_data_type);
//
//				animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.DELAY);
//			}
//			else
            if (m_data_type == ANIMATION_DATA_TYPE.DELAY ||
                m_data_type == ANIMATION_DATA_TYPE.DURATION)
            {
                return(GetFloatVariable(m_data_type, letterAction).GetStateListeners());
            }
            else if (m_data_type == ANIMATION_DATA_TYPE.POSITION ||
                     m_data_type == ANIMATION_DATA_TYPE.GLOBAL_SCALE ||
                     m_data_type == ANIMATION_DATA_TYPE.LOCAL_SCALE ||
                     m_data_type == ANIMATION_DATA_TYPE.GLOBAL_ROTATION ||
                     m_data_type == ANIMATION_DATA_TYPE.LOCAL_ROTATION)
            {
                return(GetVector3Variable(m_data_type, letterAction).GetStateListeners());
            }
            else if (m_data_type == ANIMATION_DATA_TYPE.COLOUR)
            {
                return(m_startState ? letterAction.m_start_colour.GetStateListeners() : letterAction.m_end_colour.GetStateListeners());
            }
            else if (m_data_type == ANIMATION_DATA_TYPE.DELAY_EASED_RANDOM_SWITCH)
            {
                return(new List <VariableStateListener>()
                {
                    new VariableStateListener()
                    {
                        m_startToggleValue = letterAction.m_delay_progression.Progression == (int)ValueProgression.Random, m_onToggleStateChangeCallback = (bool state) => {
                            if (state)
                            {
                                letterAction.m_delay_progression.SetRandom(letterAction.m_delay_progression.ValueFrom, letterAction.m_delay_progression.ValueTo, letterAction.m_delay_progression.UniqueRandomRaw);
                            }
                            else
                            {
                                letterAction.m_delay_progression.SetEased(letterAction.m_delay_progression.ValueFrom, letterAction.m_delay_progression.ValueTo);
                            }

                            animation_manager.PrepareAnimationData(ANIMATION_DATA_TYPE.DELAY);
                        }
                    }
                });
            }

            return(null);
        }
		void DrawLoopTree(LetterAnimation selected_animation)
		{
			float main_editor_height = Instance.position.height - WINDOW_BORDER_TOP - WINDOW_BORDER_BOTTOM;
			int num_actions = selected_animation.NumActions;
			float gui_y_offset = WINDOW_BORDER_TOP;
			int action_idx = 0;
			
			gui_y_offset = 100;
			
			// Draw A's
			m_state_node_rects = new Rect[num_actions];
			
			EditorGUI.LabelField(new Rect(state_overview_x, gui_y_offset, 100, LINE_HEIGHT*2), "Loops [" + selected_animation.NumLoops + "]", EditorStyles.boldLabel);
			gui_y_offset += LINE_HEIGHT*2;
			
			if(GUI.Button(new Rect(state_overview_x, gui_y_offset, 60, LINE_HEIGHT), new GUIContent(m_display_loops_tree ? "Hide" : "Show", (m_display_loops_tree ? "Hide" : "Show") + " the loops setup menu.")))
			{
				m_display_loops_tree = !m_display_loops_tree;
			}
			gui_y_offset += LINE_HEIGHT * 2;
			
			
			LOOP_SCROLL_POS = GUI.BeginScrollView(
				new Rect(state_overview_x, gui_y_offset, MainEditorX - state_overview_x - 14, main_editor_height - (gui_y_offset - WINDOW_BORDER_TOP)),
				LOOP_SCROLL_POS,
				new Rect(0,0, MainEditorX - 35, selected_animation.NumActions * ACTION_NODE_SPACING)
			);
			
			gui_y_offset = 0;
			
			for(action_idx = 0; action_idx < selected_animation.NumActions; action_idx++)
			{
				GUI.Label(new Rect(20, gui_y_offset, 40, 20), "A" + action_idx, EditorStyles.boldLabel);

				if(selected_animation.GetAction(action_idx).m_action_type == ACTION_TYPE.BREAK)
					GUI.DrawTexture(new Rect(5, gui_y_offset, 15, 15), m_pause_button_texture);

				if(m_display_loops_tree)
				{
					m_state_node_rects[action_idx] = new Rect(20 - ACTION_NODE_MARGIN, gui_y_offset - ACTION_NODE_MARGIN, 20 + (2 * ACTION_NODE_MARGIN), 20 + (2 * ACTION_NODE_MARGIN));
				}
				
				gui_y_offset += ACTION_NODE_SPACING;
			}
			
			
			if(m_display_loops_tree)
			{
				// Draw existing loops
				Vector2 point1, point2;
				
				// Draw Loop assignment-in-progress line, triggered by users click and drag on action nodes
				if(m_mouse_down_node_idx >= 0)
				{
					TextFxHelperMethods.DrawGUILine(m_mouse_down_pos, new Vector2(m_mouse_down_pos.x, m_mouse_drag_pos.y), Color.gray, 3);
				}
				
				state_overview_width = 0;
				
				int num_loops = selected_animation.NumLoops;
				GUIStyle my_style = EditorStyles.miniButton;
				my_style.alignment = TextAnchor.MiddleLeft;
				my_style.normal.textColor = Color.black;
				
				
				// Loop through loop list, drawing loop line representations on loop timeline and adding loop list entries
				float loops_list_x;
				ActionLoopCycle loop_cycle;
				int last_span_width = -1;
				int indent_num = 0;
				float line_offset_x = 0;
				float loop_list_line_y = 0;
				
				GUI.SetNextControlName ("LoopsTitle");
				
				// Display loop list header
				EditorGUI.LabelField(new Rect(loop_tree_width + 60, loop_list_line_y, 100, LINE_HEIGHT), "Active Loops", EditorStyles.boldLabel);
				loop_list_line_y += LINE_HEIGHT;
				
				EditorGUI.LabelField(new Rect(loop_tree_width + 52, loop_list_line_y, 30, LINE_HEIGHT), new GUIContent("From","The index of the action to start this loop."), EditorStyles.miniBoldLabel);
				EditorGUI.LabelField(new Rect(loop_tree_width + 87, loop_list_line_y, 20, LINE_HEIGHT), new GUIContent("To", "The index of the action to end this loop."), EditorStyles.miniBoldLabel);
				EditorGUI.LabelField(new Rect(loop_tree_width + 110, loop_list_line_y, 42, LINE_HEIGHT), new GUIContent("#Loops", "The number of times to run through this loop. Enter zero to run loop infinitely."), EditorStyles.miniBoldLabel);
				EditorGUI.LabelField(new Rect(loop_tree_width + 160, loop_list_line_y, 40, LINE_HEIGHT), new GUIContent("Type", "The type/behaviour of this loop."), EditorStyles.miniBoldLabel);
				EditorGUI.LabelField(new Rect(loop_tree_width + 262, loop_list_line_y, 70, LINE_HEIGHT), new GUIContent("DFO [?]", "'Delay First Only' - Letter action delays (non-constant) will only be applied for the first forward pass through the loop. This stops the letters getting more and more out of sequence with every loop interation."), EditorStyles.miniBoldLabel);

				loop_list_line_y += LINE_HEIGHT;
				
				
				for(int loop_idx=0; loop_idx < num_loops; loop_idx++)
				{
					loop_cycle = selected_animation.GetLoop(loop_idx);
					
					if(last_span_width == -1)
					{
						last_span_width = loop_cycle.SpanWidth;
					}
					
					// Check for invalid loops
					if(loop_cycle.m_start_action_idx >= num_actions || loop_cycle.m_end_action_idx >= num_actions)
					{
						// invalid loop. Delete it.
						selected_animation.RemoveLoop(loop_idx);
						loop_idx--;
						num_loops--;
						continue;
					}
					
					// Represent loop as a line on the Action timeline
					if(last_span_width != loop_cycle.SpanWidth)
					{
						last_span_width = loop_cycle.SpanWidth;
						indent_num++;
					}
					
					line_offset_x = 20 + (indent_num * LOOP_LINE_OFFSET);
					
					if(loop_cycle.m_start_action_idx != loop_cycle.m_end_action_idx)
					{
						point1 = m_state_node_rects[loop_cycle.m_start_action_idx].center + new Vector2(line_offset_x, 0);
						point2 = m_state_node_rects[loop_cycle.m_end_action_idx].center + new Vector2(line_offset_x, 0);
						
						TextFxHelperMethods.DrawGUILine(point1, point2, loop_cycle.m_loop_type == LOOP_TYPE.LOOP ? LOOP_COLOUR : LOOP_REVERSE_COLOUR, 2);
						
						TextFxHelperMethods.DrawGUILine(point1 + new Vector2(0, -2), point1 + new Vector2(0, 2), loop_cycle.m_loop_type == LOOP_TYPE.LOOP ? LOOP_COLOUR : LOOP_REVERSE_COLOUR, 6);
						TextFxHelperMethods.DrawGUILine(point2 + new Vector2(0, -2), point2 + new Vector2(0, 2), loop_cycle.m_loop_type == LOOP_TYPE.LOOP ? LOOP_COLOUR : LOOP_REVERSE_COLOUR, 6);
					}
					else
					{
						point1 = m_state_node_rects[loop_cycle.m_start_action_idx].center + new Vector2(line_offset_x, -2);
						point2 = m_state_node_rects[loop_cycle.m_end_action_idx].center + new Vector2(line_offset_x, 2);
						
						TextFxHelperMethods.DrawGUILine(point1, point2, loop_cycle.m_loop_type == LOOP_TYPE.LOOP ? LOOP_COLOUR : LOOP_REVERSE_COLOUR, 6);
					}
					
					// Display loop number
					my_style.normal.textColor = loop_cycle.m_loop_type == LOOP_TYPE.LOOP ? LOOP_COLOUR : LOOP_REVERSE_COLOUR;
					EditorGUI.LabelField(new Rect(point1.x, (point1.y + point2.y) / 2 - 10, loop_cycle.m_number_of_loops > 9 ? 30 : 20, 20),  loop_cycle.m_number_of_loops <= 0 ? "~" : "" + loop_cycle.m_number_of_loops, my_style); //EditorStyles.miniButton);
					
					bool gui_changed = GUI.changed;

					// Display list view of loop cycle
					loops_list_x = loop_tree_width;
					EditorGUI.LabelField(new Rect(loops_list_x, loop_list_line_y, 100, LINE_HEIGHT), "Loop " + (loop_idx+1));
					loops_list_x += 58;
					EditorGUI.LabelField(new Rect(loops_list_x, loop_list_line_y, 20, LINE_HEIGHT), "" + loop_cycle.m_start_action_idx);
					loops_list_x += 30;
					EditorGUI.LabelField(new Rect(loops_list_x, loop_list_line_y, 20, LINE_HEIGHT), "" + loop_cycle.m_end_action_idx);
					loops_list_x += 32;
					loop_cycle.m_number_of_loops = Mathf.Max( EditorGUI.IntField(new Rect(loops_list_x, loop_list_line_y, 20, LINE_HEIGHT), loop_cycle.m_number_of_loops), 0);
					loops_list_x += 30;
					gui_changed = GUI.changed;
					loop_cycle.m_loop_type = (LOOP_TYPE) EditorGUI.EnumPopup(new Rect(loops_list_x, loop_list_line_y, 70, LINE_HEIGHT), loop_cycle.m_loop_type);
					if(!gui_changed && GUI.changed)
						m_animation_manager.PrepareAnimationData();
					loops_list_x += 90;
					if(loop_cycle.m_loop_type == LOOP_TYPE.LOOP_REVERSE)
					{
						gui_changed = GUI.changed;
						loop_cycle.m_finish_at_end = EditorGUI.Toggle(new Rect(loops_list_x, loop_list_line_y, 20, LINE_HEIGHT), loop_cycle.m_finish_at_end);
						if(!gui_changed && GUI.changed)
							m_animation_manager.PrepareAnimationData();
					}
					loops_list_x += 30;
					if(loop_cycle.m_loop_type == LOOP_TYPE.LOOP_REVERSE)
						loop_cycle.m_delay_first_only = EditorGUI.Toggle(new Rect(loops_list_x, loop_list_line_y, 20, LINE_HEIGHT), loop_cycle.m_delay_first_only);
					loops_list_x += 30;
					if(GUI.Button(new Rect(loops_list_x, loop_list_line_y, 24, LINE_HEIGHT), "x") && m_animation_manager.WipeQuickSetupData(user_confirm: true))
					{
						selected_animation.RemoveLoop(loop_idx);
						num_loops--;

						m_animation_manager.PrepareAnimationData();
						continue;
					}
					
					loop_list_line_y += LINE_HEIGHT;
					
				}
				
				loop_list_line_y += 5;
				
				// "Add new loop" line
				loops_list_x = loop_tree_width;
				EditorGUI.LabelField(new Rect(loops_list_x, loop_list_line_y, 100, LINE_HEIGHT), "New", EditorStyles.boldLabel);
				loops_list_x += 58;
				new_loop_from = EditorGUI.IntField(new Rect(loops_list_x, loop_list_line_y, 20, LINE_HEIGHT), new_loop_from);
				loops_list_x += 30;
				new_loop_to = EditorGUI.IntField(new Rect(loops_list_x, loop_list_line_y, 20, LINE_HEIGHT), new_loop_to);
				loops_list_x += 32;
				if(GUI.Button(new Rect(loops_list_x, loop_list_line_y, 40, LINE_HEIGHT), "Add") && m_animation_manager.WipeQuickSetupData(user_confirm: true))
				{
					selected_animation.AddLoop(new_loop_from, new_loop_to, false);
					m_animation_manager.PrepareAnimationData();
					
					new_loop_from = 0;
					new_loop_to = 0;
					
					// Force keyboard focus loss from any of the loop adding fields.
					GUI.FocusControl("LoopsTitle");
				}
				
				// Set the width of the loop tree segment
				loop_tree_width = (int) line_offset_x + BASE_LOOP_LIST_POSITION_OFFSET;
				
				// Add additional width of loop list menu
				state_overview_width = loop_tree_width + LOOP_LIST_WIDTH;
				
				
				EventType eventType = Event.current.type;
				Vector2 mousePos = Event.current.mousePosition;
				
			    if (eventType == EventType.MouseDown && mousePos.x < MainEditorX)
			    {
					int rect_idx = 0;
					foreach(Rect node_rect in m_state_node_rects)
					{
						if(node_rect.Contains(mousePos))
						{
							m_mouse_down_node_idx= rect_idx;
							m_mouse_down_pos = node_rect.center;
							m_mouse_drag_pos = m_mouse_down_pos;
						}
						
						rect_idx ++;
					}
			    }
				else if(eventType == EventType.MouseDrag)
				{
					if(m_mouse_down_node_idx >= 0)
					{
						m_mouse_drag_pos = mousePos;
						
						Instance.Repaint();
					}
				}
				else if(eventType == EventType.MouseUp)
				{
					if(m_mouse_down_node_idx >= 0 && mousePos.x < MainEditorX)
					{
						int rect_idx = 0;
						foreach(Rect node_rect in m_state_node_rects)
						{
							if(node_rect.Contains(mousePos))
							{
	//							Debug.LogError("you joined : " + m_mouse_down_node_idx + " and " + rect_idx + " with button " + Event.current.button);
								
								int start, end;
								if(m_mouse_down_node_idx < rect_idx)
								{
									start = m_mouse_down_node_idx;
									end = rect_idx;
								}
								else
								{
									start = rect_idx;
									end = m_mouse_down_node_idx;
								}
								
								selected_animation.AddLoop(start, end, Event.current.button == 1);
	//							m_animation_manager.PrepareAnimationData();
								
								break;
							}
							
							rect_idx ++;
						}
					}
					
					m_mouse_down_node_idx = -1;
					Instance.Repaint();
				}
			}
			else
			{
				state_overview_width = 0;
			}
			
			GUI.EndScrollView();
		}