From 5fa7b2a347f8906ba5fcd6977e2001078fb5998a Mon Sep 17 00:00:00 2001 From: chrisgregan Date: Tue, 2 Sep 2014 20:06:05 +0100 Subject: [PATCH] Reorderable list for commands --- .../Editor/FungusCommandEditor.cs | 53 +++-- .../Editor/FungusCommandListAdaptor.cs | 195 ++++++++++++++++++ .../Editor/FungusCommandListAdaptor.cs.meta | 8 + .../FungusScript/Editor/FungusScriptEditor.cs | 115 ----------- .../FungusScript/Editor/FungusScriptWindow.cs | 17 +- .../Editor/FungusVariableListAdaptor.cs | 3 +- .../FungusScript/Editor/SequenceEditor.cs | 130 ++++++++++++ .../Fungus/FungusScript/Scripts/Sequence.cs | 11 +- Assets/Shuttle/ShuttleGame.unity | Bin 116700 -> 117228 bytes 9 files changed, 375 insertions(+), 157 deletions(-) create mode 100644 Assets/Fungus/FungusScript/Editor/FungusCommandListAdaptor.cs create mode 100644 Assets/Fungus/FungusScript/Editor/FungusCommandListAdaptor.cs.meta diff --git a/Assets/Fungus/FungusScript/Editor/FungusCommandEditor.cs b/Assets/Fungus/FungusScript/Editor/FungusCommandEditor.cs index 98e08854..f6d0dd5f 100644 --- a/Assets/Fungus/FungusScript/Editor/FungusCommandEditor.cs +++ b/Assets/Fungus/FungusScript/Editor/FungusCommandEditor.cs @@ -103,27 +103,18 @@ namespace Fungus.Script GUILayout.BeginHorizontal(); - string commandName = FungusScriptEditor.GetCommandName(t.GetType()); - GUILayout.Label(commandName, EditorStyles.largeLabel); - - GUILayout.FlexibleSpace(); - - bool enabled = GUILayout.Toggle(t.enabled, ""); + bool enabled = GUILayout.Toggle(t.enabled, new GUIContent()); if (t.enabled != enabled) { Undo.RecordObject(t, "Set Enabled"); t.enabled = enabled; } - if (GUILayout.Button("Up", EditorStyles.miniButtonLeft)) - { - UnityEditorInternal.ComponentUtility.MoveComponentUp(t); - } - if (GUILayout.Button("Down", EditorStyles.miniButtonMid)) - { - UnityEditorInternal.ComponentUtility.MoveComponentDown(t); - } - + string commandName = FungusScriptEditor.GetCommandName(t.GetType()); + GUILayout.Label(commandName + " Command", EditorStyles.boldLabel); + + GUILayout.FlexibleSpace(); + if (fungusScript != null) { if (GUILayout.Button("Copy", EditorStyles.miniButtonMid)) @@ -133,18 +124,16 @@ namespace Fungus.Script if (fungusScript.copyCommand != null) { - if (GUILayout.Button("Paste", EditorStyles.miniButtonMid)) + if (GUILayout.Button("Paste", EditorStyles.miniButton)) { - CopyComponent(fungusScript.copyCommand, t.gameObject); + Sequence parentSequence = t.GetComponent(); + if (parentSequence != null) + { + PasteCommand(fungusScript.copyCommand, parentSequence); + } } } } - - if (GUILayout.Button("Delete", EditorStyles.miniButtonRight)) - { - Undo.DestroyObjectImmediate(t); - return; - } GUILayout.EndHorizontal(); @@ -169,16 +158,24 @@ namespace Fungus.Script DrawDefaultInspector(); } - T CopyComponent(T original, GameObject destination) where T : Component + static public FungusCommand PasteCommand(FungusCommand copyCommand, Sequence sequence) { - System.Type type = original.GetType(); - Component copy = Undo.AddComponent(destination, type); + System.Type type = copyCommand.GetType(); + Component copy = Undo.AddComponent(sequence.gameObject, type); System.Reflection.FieldInfo[] fields = type.GetFields(); foreach (System.Reflection.FieldInfo field in fields) { - field.SetValue(copy, field.GetValue(original)); + field.SetValue(copy, field.GetValue(copyCommand)); } - return copy as T; + + FungusScript fungusScript = sequence.GetFungusScript(); + + Undo.RecordObject(fungusScript, "Paste Command"); + + FungusCommand newCommand = copy as FungusCommand; + sequence.commandList.Add(newCommand); + + return newCommand; } static public T ObjectField(GUIContent label, GUIContent nullLabel, T selectedObject, List objectList) where T : MonoBehaviour diff --git a/Assets/Fungus/FungusScript/Editor/FungusCommandListAdaptor.cs b/Assets/Fungus/FungusScript/Editor/FungusCommandListAdaptor.cs new file mode 100644 index 00000000..9c80acf2 --- /dev/null +++ b/Assets/Fungus/FungusScript/Editor/FungusCommandListAdaptor.cs @@ -0,0 +1,195 @@ +// Copyright (c) 2012-2013 Rotorz Limited. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +using UnityEngine; +using UnityEditor; +using System; +using Rotorz.ReorderableList; + +namespace Fungus.Script +{ + public class FungusCommandListAdaptor : IReorderableListAdaptor { + + private SerializedProperty _arrayProperty; + + public float fixedItemHeight; + + public SerializedProperty this[int index] { + get { return _arrayProperty.GetArrayElementAtIndex(index); } + } + + public SerializedProperty arrayProperty { + get { return _arrayProperty; } + } + + public FungusCommandListAdaptor(SerializedProperty arrayProperty, float fixedItemHeight) { + if (arrayProperty == null) + throw new ArgumentNullException("Array property was null."); + if (!arrayProperty.isArray) + throw new InvalidOperationException("Specified serialized propery is not an array."); + + this._arrayProperty = arrayProperty; + this.fixedItemHeight = fixedItemHeight; + } + + public FungusCommandListAdaptor(SerializedProperty arrayProperty) : this(arrayProperty, 0f) { + } + + public int Count { + get { return _arrayProperty.arraySize; } + } + + public virtual bool CanDrag(int index) { + return true; + } + + public virtual bool CanRemove(int index) { + return true; + } + + public void Add() { + int newIndex = _arrayProperty.arraySize; + ++_arrayProperty.arraySize; + ResetValue(_arrayProperty.GetArrayElementAtIndex(newIndex)); + } + + public void Insert(int index) { + _arrayProperty.InsertArrayElementAtIndex(index); + ResetValue(_arrayProperty.GetArrayElementAtIndex(index)); + } + + public void Duplicate(int index) { + _arrayProperty.InsertArrayElementAtIndex(index); + } + + public void Remove(int index) { + // Remove the Fungus Command component + FungusCommand command = _arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue as FungusCommand; + Undo.DestroyObjectImmediate(command); + + _arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue = null; + _arrayProperty.DeleteArrayElementAtIndex(index); + } + + public void Move(int sourceIndex, int destIndex) { + if (destIndex > sourceIndex) + --destIndex; + _arrayProperty.MoveArrayElement(sourceIndex, destIndex); + } + + public void Clear() { + _arrayProperty.ClearArray(); + } + + public void DrawItem(Rect position, int index) + { + FungusCommand command = this[index].objectReferenceValue as FungusCommand; + + FungusScript fungusScript = command.GetFungusScript(); + + bool error = false; + string summary = command.GetSummary().Replace("\n", "").Replace("\r", ""); + if (summary.Length > 80) + { + summary = summary.Substring(0, 80) + "..."; + } + if (summary.StartsWith("Error:")) + { + error = true; + } + + if (!command.enabled) + { + GUI.backgroundColor = Color.grey; + } + else if (error) + { + GUI.backgroundColor = Color.red; + } + else if (fungusScript.selectedCommand == command) + { + GUI.backgroundColor = Color.yellow; + } + + Rect buttonRect = position; + buttonRect.width = 80; + + Rect summaryRect = position; + summaryRect.x += 85; + summaryRect.width -= 85; + + string commandName = FungusScriptEditor.GetCommandName(command.GetType()); + if (GUI.Button(buttonRect, commandName, EditorStyles.miniButton)) + { + fungusScript.selectedCommand = command; + GUIUtility.keyboardControl = 0; // Fix for textarea not refeshing (change focus) + } + GUI.backgroundColor = Color.white; + + GUIStyle labelStyle = new GUIStyle(EditorStyles.miniLabel); + labelStyle.wordWrap = true; + if (!command.enabled) + { + labelStyle.normal.textColor = Color.grey; + } + else if (error) + { + labelStyle.normal.textColor = Color.red; + } + + GUI.Label(summaryRect, summary, labelStyle); + + if (Event.current.type == EventType.Repaint && + command.IsExecuting()) + { + GLDraw.DrawBox(position, Color.green, 1.5f); + } + } + + public virtual float GetItemHeight(int index) { + return fixedItemHeight != 0f + ? fixedItemHeight + : EditorGUI.GetPropertyHeight(this[index], GUIContent.none, false) + ; + } + + private void ResetValue(SerializedProperty element) { + switch (element.type) { + case "string": + element.stringValue = ""; + break; + case "Vector2f": + element.vector2Value = Vector2.zero; + break; + case "Vector3f": + element.vector3Value = Vector3.zero; + break; + case "Rectf": + element.rectValue = new Rect(); + break; + case "Quaternionf": + element.quaternionValue = Quaternion.identity; + break; + case "int": + element.intValue = 0; + break; + case "float": + element.floatValue = 0f; + break; + case "UInt8": + element.boolValue = false; + break; + case "ColorRGBA": + element.colorValue = Color.black; + break; + + default: + if (element.type.StartsWith("PPtr")) + element.objectReferenceValue = null; + break; + } + } + } +} + diff --git a/Assets/Fungus/FungusScript/Editor/FungusCommandListAdaptor.cs.meta b/Assets/Fungus/FungusScript/Editor/FungusCommandListAdaptor.cs.meta new file mode 100644 index 00000000..edfe930d --- /dev/null +++ b/Assets/Fungus/FungusScript/Editor/FungusCommandListAdaptor.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 44d3aa3e5c3bf4b99b9899e8d5f5bc52 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Fungus/FungusScript/Editor/FungusScriptEditor.cs b/Assets/Fungus/FungusScript/Editor/FungusScriptEditor.cs index 72d6fbce..a9d807da 100644 --- a/Assets/Fungus/FungusScript/Editor/FungusScriptEditor.cs +++ b/Assets/Fungus/FungusScript/Editor/FungusScriptEditor.cs @@ -2,7 +2,6 @@ using UnityEditor; using UnityEngine; using System.Collections; using System.Collections.Generic; -using Fungus; using Rotorz.ReorderableList; using System.Linq; @@ -74,120 +73,6 @@ namespace Fungus.Script DrawVariablesGUI(); } - public void DrawSequenceGUI(FungusScript fungusScript) - { - if (fungusScript.selectedSequence == null) - { - return; - } - - Sequence sequence = fungusScript.selectedSequence; - - EditorGUI.BeginChangeCheck(); - - string name = EditorGUILayout.TextField(new GUIContent("Name", "Name of sequence displayed in editor window"), sequence.name); - string desc = EditorGUILayout.TextField(new GUIContent("Description", "Sequence description displayed in editor window"), sequence.description); - - EditorGUILayout.Separator(); - - if (name != sequence.name) - { - // The name is the gameobject name, so have to undo seperately - Undo.RecordObject(sequence.gameObject, "Set Sequence Name"); - sequence.name = name; - } - - if (desc != sequence.description) - { - Undo.RecordObject(sequence, "Set Sequence Description"); - sequence.description = desc; - } - - GUILayout.Box("Commands", GUILayout.ExpandWidth(true)); - - FungusCommand[] commands = sequence.GetComponents(); - foreach (FungusCommand command in commands) - { - FungusCommandEditor commandEditor = Editor.CreateEditor(command) as FungusCommandEditor; - commandEditor.DrawCommandRowGUI(); - } - - if (Application.isPlaying) - { - return; - } - - EditorGUILayout.Separator(); - - GUILayout.Box("New Command", GUILayout.ExpandWidth(true)); - - EditorGUI.BeginChangeCheck(); - - EditorGUILayout.BeginHorizontal(); - - List commandNames = new List(); - List commandTypes = EditorExtensions.FindDerivedTypes(typeof(FungusCommand)).ToList(); - - foreach (System.Type type in commandTypes) - { - object[] attributes = type.GetCustomAttributes(false); - foreach (object obj in attributes) - { - CommandCategoryAttribute categoryAttr = obj as CommandCategoryAttribute; - if (categoryAttr != null) - { - string commandItem = categoryAttr.Category + " / " + GetCommandName(type); - commandNames.Add(commandItem); - break; - } - } - } - - int selectedCommandIndex = EditorGUILayout.Popup(fungusScript.selectedAddCommandIndex, commandNames.ToArray()); - - if (EditorGUI.EndChangeCheck()) - { - Undo.RecordObject(fungusScript, "Select Command"); - fungusScript.selectedAddCommandIndex = selectedCommandIndex; - } - - if (selectedCommandIndex >= commandTypes.Count) - { - EditorGUILayout.EndHorizontal(); - return; - } - - System.Type selectedType = commandTypes[selectedCommandIndex]; - if (fungusScript.selectedSequence == null || - selectedType == null) - { - EditorGUILayout.EndHorizontal(); - return; - } - - if (GUILayout.Button(new GUIContent("Add", "Add the selected command to the sequence"), EditorStyles.miniButton)) - { - Undo.AddComponent(fungusScript.selectedSequence.gameObject, selectedType); - } - - EditorGUILayout.EndHorizontal(); - - object[] helpAttributes = selectedType.GetCustomAttributes(typeof(HelpTextAttribute), false); - foreach (object obj in helpAttributes) - { - HelpTextAttribute helpTextAttr = obj as HelpTextAttribute; - if (helpTextAttr != null) - { - GUIStyle labelStyle = new GUIStyle(EditorStyles.miniLabel); - labelStyle.wordWrap = true; - EditorGUILayout.HelpBox(helpTextAttr.HelpText, MessageType.Info); - break; - } - } - - EditorGUILayout.Separator(); - } - public void DrawVariablesGUI() { serializedObject.Update(); diff --git a/Assets/Fungus/FungusScript/Editor/FungusScriptWindow.cs b/Assets/Fungus/FungusScript/Editor/FungusScriptWindow.cs index f09d356b..f4005e97 100755 --- a/Assets/Fungus/FungusScript/Editor/FungusScriptWindow.cs +++ b/Assets/Fungus/FungusScript/Editor/FungusScriptWindow.cs @@ -51,7 +51,7 @@ namespace Fungus.Script GUILayout.BeginHorizontal(); DrawScriptView(fungusScript); ResizeViews(fungusScript); - DrawCommandView(fungusScript); + DrawSequenceView(fungusScript); GUILayout.EndHorizontal(); } @@ -179,7 +179,7 @@ namespace Fungus.Script } } - void DrawCommandView(FungusScript fungusScript) + void DrawSequenceView(FungusScript fungusScript) { GUILayout.Space(5); @@ -235,13 +235,16 @@ namespace Fungus.Script } GUILayout.EndHorizontal(); - - EditorGUILayout.Separator(); - FungusScriptEditor editor = Editor.CreateEditor(fungusScript) as FungusScriptEditor; - editor.DrawSequenceGUI(fungusScript); + if (fungusScript.selectedSequence != null) + { + EditorGUILayout.Separator(); + + SequenceEditor editor = Editor.CreateEditor(fungusScript.selectedSequence) as SequenceEditor; + editor.DrawSequenceGUI(fungusScript); - GUILayout.FlexibleSpace(); + GUILayout.FlexibleSpace(); + } EditorGUILayout.EndVertical(); diff --git a/Assets/Fungus/FungusScript/Editor/FungusVariableListAdaptor.cs b/Assets/Fungus/FungusScript/Editor/FungusVariableListAdaptor.cs index d208fa1f..c73f077a 100644 --- a/Assets/Fungus/FungusScript/Editor/FungusVariableListAdaptor.cs +++ b/Assets/Fungus/FungusScript/Editor/FungusVariableListAdaptor.cs @@ -198,8 +198,7 @@ namespace Fungus.Script if (fungusScript.selectedSequence != null && fungusScript.selectedCommand != null) { - FungusCommand[] commands = fungusScript.selectedSequence.GetComponents(); - foreach (FungusCommand command in commands) + foreach (FungusCommand command in fungusScript.selectedSequence.commandList) { if (fungusScript.selectedCommand == command && command.HasReference(variable)) diff --git a/Assets/Fungus/FungusScript/Editor/SequenceEditor.cs b/Assets/Fungus/FungusScript/Editor/SequenceEditor.cs index 4477f37b..4cb230d4 100644 --- a/Assets/Fungus/FungusScript/Editor/SequenceEditor.cs +++ b/Assets/Fungus/FungusScript/Editor/SequenceEditor.cs @@ -3,6 +3,8 @@ using UnityEngine; using System; using System.Collections; using System.Collections.Generic; +using System.Linq; +using Rotorz.ReorderableList; namespace Fungus.Script { @@ -10,6 +12,134 @@ namespace Fungus.Script [CustomEditor (typeof(Sequence))] public class SequenceEditor : Editor { + public void DrawSequenceGUI(FungusScript fungusScript) + { + if (fungusScript.selectedSequence == null) + { + return; + } + + serializedObject.Update(); + + Sequence sequence = fungusScript.selectedSequence; + + EditorGUI.BeginChangeCheck(); + + string name = EditorGUILayout.TextField(new GUIContent("Name", "Name of sequence displayed in editor window"), sequence.name); + string desc = EditorGUILayout.TextField(new GUIContent("Description", "Sequence description displayed in editor window"), sequence.description); + + EditorGUILayout.Separator(); + + if (name != sequence.name) + { + // The name is the gameobject name, so have to undo seperately + Undo.RecordObject(sequence.gameObject, "Set Sequence Name"); + sequence.name = name; + } + + if (desc != sequence.description) + { + Undo.RecordObject(sequence, "Set Sequence Description"); + sequence.description = desc; + } + + + ReorderableListGUI.Title("Command Sequence"); + + SerializedProperty commandListProperty = serializedObject.FindProperty("commandList"); + FungusCommandListAdaptor adaptor = new FungusCommandListAdaptor(commandListProperty, 0); + ReorderableListControl.DrawControlFromState(adaptor, null, ReorderableListFlags.DisableContextMenu | ReorderableListFlags.HideAddButton); + + if (Application.isPlaying) + { + return; + } + + EditorGUI.BeginChangeCheck(); + + EditorGUILayout.Separator(); + + EditorGUILayout.BeginHorizontal(); + + List commandNames = new List(); + List commandTypes = EditorExtensions.FindDerivedTypes(typeof(FungusCommand)).ToList(); + + foreach (System.Type type in commandTypes) + { + object[] attributes = type.GetCustomAttributes(false); + foreach (object obj in attributes) + { + CommandCategoryAttribute categoryAttr = obj as CommandCategoryAttribute; + if (categoryAttr != null) + { + string commandItem = categoryAttr.Category + " / " + FungusScriptEditor.GetCommandName(type); + commandNames.Add(commandItem); + break; + } + } + } + + Rect newRect = GUILayoutUtility.GetRect(100, 30); + + int selectedCommandIndex = EditorGUI.Popup(newRect, fungusScript.selectedAddCommandIndex, commandNames.ToArray()); + + if (EditorGUI.EndChangeCheck()) + { + Undo.RecordObject(fungusScript, "Select Command"); + fungusScript.selectedAddCommandIndex = selectedCommandIndex; + } + + if (selectedCommandIndex >= commandTypes.Count) + { + EditorGUILayout.EndHorizontal(); + return; + } + + System.Type selectedType = commandTypes[selectedCommandIndex]; + if (fungusScript.selectedSequence == null || + selectedType == null) + { + EditorGUILayout.EndHorizontal(); + return; + } + + if (GUILayout.Button(new GUIContent("Add Command", "Add the selected command to the sequence"), EditorStyles.miniButton)) + { + FungusCommand newCommand = Undo.AddComponent(fungusScript.selectedSequence.gameObject, selectedType) as FungusCommand; + Undo.RecordObject(fungusScript, "Add Command"); + fungusScript.selectedSequence.commandList.Add(newCommand); + } + + /* + if (fungusScript.copyCommand != null) + { + if (GUILayout.Button("Paste", EditorStyles.miniButton)) + { + FungusCommandEditor.PasteCommand(fungusScript.copyCommand, fungusScript.selectedSequence); + } + } + */ + + EditorGUILayout.EndHorizontal(); + + object[] helpAttributes = selectedType.GetCustomAttributes(typeof(HelpTextAttribute), false); + foreach (object obj in helpAttributes) + { + HelpTextAttribute helpTextAttr = obj as HelpTextAttribute; + if (helpTextAttr != null) + { + GUIStyle labelStyle = new GUIStyle(EditorStyles.miniLabel); + labelStyle.wordWrap = true; + EditorGUILayout.HelpBox(helpTextAttr.HelpText, MessageType.Info); + break; + } + } + + EditorGUILayout.Separator(); + + serializedObject.ApplyModifiedProperties(); + } + static public Sequence SequenceField(GUIContent label, GUIContent nullLabel, FungusScript fungusScript, Sequence sequence) { if (fungusScript == null) diff --git a/Assets/Fungus/FungusScript/Scripts/Sequence.cs b/Assets/Fungus/FungusScript/Scripts/Sequence.cs index 2210fbb8..f256cd98 100644 --- a/Assets/Fungus/FungusScript/Scripts/Sequence.cs +++ b/Assets/Fungus/FungusScript/Scripts/Sequence.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; namespace Fungus.Script { + [ExecuteInEditMode] public class Sequence : MonoBehaviour { [HideInInspector] @@ -23,8 +24,10 @@ namespace Fungus.Script [System.NonSerialized] public FungusCommand activeCommand; + public List commandList = new List(); + int executionCount; - + public virtual void Start() { fungusScript = GetFungusScript(); @@ -48,8 +51,7 @@ namespace Fungus.Script public bool HasError() { - FungusCommand[] commands = GetComponents(); - foreach (FungusCommand command in commands) + foreach (FungusCommand command in commandList) { if (command.errorMessage.Length > 0) { @@ -87,8 +89,7 @@ namespace Fungus.Script FungusCommand nextCommand = null; bool executeNext = (currentCommand == null); - FungusCommand[] commands = GetComponents(); - foreach (FungusCommand command in commands) + foreach (FungusCommand command in commandList) { if (command == currentCommand) { diff --git a/Assets/Shuttle/ShuttleGame.unity b/Assets/Shuttle/ShuttleGame.unity index 30447e9e0923c585ea5968ad2e850fa0cbe473e7..1243a7d694488be7be9aaf815188b5c8265305fd 100644 GIT binary patch delta 5908 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 GUGsmb(2BGG delta 5664 zcmZA53vg7`0mkuj1vNN|t3}yJfQteK5|R}KqEfS>=mvyrAi{21nrVLW7qAx^qcFV z*m}$m!jk`pUx2P0AAKE^T(f=m@LsHpoNQl~ zYq8u>dLQ{$Sf__az#1hu>59|CQ<&@|X_o>lI9iBS-hX3XNg>vUF&=TuE+vH%+%8jy zRlIjPo`prQZCKJSvt@3K5Frcr&TjAl83fe^Z^)kO?!eZPGwd<~bKj^BFb8YHLP~oh zv8M~VeDNJ3aMOg4=eVHn;K1^+| z<^23Hu3RCWV*d?H>lUyBG#R0s9$B_xhisD_$|B4gLPp!WSmV{_{!DedogE?<&t=? z+Yk9os-5#MI}U(;PkK6?dq4%&h3)0@uj~f9J`@xEx9C^*2G)tGQ$4FCo+l>j zx!G6%yO?AC-`SDO>jKry^*hXcs}RfS_N=VCqz##5}j@xm`oc zJgpd4JTTL{u|5k_Zw!0D$P^(SAoyRq=4#UW#=3xG2iX_8vkkJsVPBBotY!p zAxxP;OYAG(Gu3^E9()N4VMFz z>#uflbno-iJErTwkFb7B%^j)xs@VTyJ@;p<6H{{)3rf7=F8%vp5f;MsGlNNa{*#nS z&|Rz_r^Q$+rXHOqw=ce@6H~8b6DGWR$G`n!U}gY&h_>bFlI1O|2a{jr@7u+C8}!_* zAG>1*-}Xf@wUgsm3RACcMd=GA-WhylUn0b3gVJUDxu2K#?$tZqfpubP-JO`2$^AFT zNZ9BX9zb^0Pns2+sdu~^JA|o~zdtc`w);Lk_X8Hf)ZBhEdrlnd;_{sVo_U~RPS_{J z%k*nF_y4n?S)x}yCxf;*4;KAcFDCDB&J)eovhJ+((tT-P$TwINi;#03UcSX#vvvE< zl0k4Y!9Kh4dCbirZc{41`BGkK7p5*rH%-Gg~6F77u=KKI`%gWz7J!I@Zak-pRK z!+NkkC^;V%sZ6iCb4p6E6vk{1?1eqk_Al@**1yDJ)Am~nIzc(YFYGmLBWSYQ;DO3J z^50t+!5+t)S=fn*CHjL(6l=wzgg&?1`zhwG(s%#QWDd)b=l>Xu%JR=KpDyYI(>I26 zM;~MUYJFNi!46^a>YOV+fw`CIS9}r+VXT1ow>_9sSR5O;f5`R!R91dih$Fmq)-#2r zFxkn!?8={E-sLGMCBj{ZJm z)6zsn@I%bQkktkc96YnM6;m5Lh$S;}Yg#_8DtbiEJ&HxKi|sN0#s{K01=e12@k^rO z^xW#e8olx&tP8tXnH$l>2bX0%s^?0uDooAYuy^;eo{ZcG%w^T-!I3LpTo%OCbsB{w zF!laD8uI}g?PW{y+r;8n4zHa}{r1Vchf|pP4LE1T%MZ74?pw%R`1?<8h`FHIpdY7F ztP@k8`~5PvP9IDe7RCIu<2?7z!;+XfnBFh>|A;2-5zZ3tf7k+xdaf6XVi)?A=U?vrhpuW4r80s`Fz;h}$4`tJy0#5dJGQXCjNGb} zcdd^peTTN4^Zp3t;ufl2u(cWtf}06`W}od^tPg9%Qnsy;xhHgc6iZ^VLFZ0bhZS*E za_OA9A*>VQbc;{z_SRyq2G@^rJ2a9>xvB)?wF&1tyu8h^ne>S%m{}@qtj}W&};xmNEoiT(dsHkZ$OU&vu zV`5*n8hPFCZ!sc6hRX>|l#QO@8FYl8^Rp+mI%;G;d*U)z_pYen$%<7yZsf%LyN$u! zInNnaUV4@MPM42X^5;}+%8SOi*uhyuLhVK>*41u|jcx2OZd*~!$8+n5MA3e2XO y`t@c*ZC!J*Sstugw=&pB6UAm}FyCyhsjV##-FNLXmW+rU`HkW49`=@zxbpuIt5)m)