/// <summary>
 /// Spawn a new group at a random location.
 /// </summary>
 public GroupCrystallonEntity spawn(AbstractCrystallonEntity[] pMembers)
 {
     var _screenWidth = Director.Instance.GL.Context.GetViewport().Width;
     var _screenHeight = Director.Instance.GL.Context.GetViewport().Height;
     return spawn( 50f + 0.75f * _screenWidth * GameScene.Random.NextFloat(),
            50f + 0.75f * _screenHeight * GameScene.Random.NextFloat(),
                  pMembers);
 }
        /// <summary>
        /// Spawn a new group the specified pX and pY.
        /// </summary>
        /// <param name='pX'>
        /// X coordinate
        /// </param>
        /// <param name='pY'>
        /// Y coordinate
        /// </param>
        public GroupCrystallonEntity spawn( float pX, float pY, AbstractCrystallonEntity[] pMembers, bool pComplete = false )
        {
            GroupCrystallonEntity g;
            if (pComplete) {
                g = new CubeCrystallonEntity(_scene, _physics, null);
            } else {
                g = new GroupCrystallonEntity(_scene, _physics, null, SelectionGroup.MAX_CAPACITY, pComplete);
            }
            foreach (AbstractCrystallonEntity e in pMembers) {
                g.Add(e);
            }
            g.id = NextId;
            NextId += 1;
            g.setPosition( pX, pY );
            g.BeReleased(g.getPosition());

            EventHandler handler = GroupSpawned;
            if (handler != null) {
                handler( this, null );
            }

            return g;
            //			g.addToScene();
            //			return Add(g);
        }
 /// <summary>
 /// Called we're only releasing a single entity.
 /// </summary>
 /// <returns>
 /// The CardCrystallonEntity
 /// </returns>
 protected virtual AbstractCrystallonEntity ReleaseSingle( AbstractCrystallonEntity pEntity )
 {
     Remove( pEntity );
     return pEntity.BeReleased( this.getPosition() );
 }
 /// <summary>
 /// If an entity has a fixed orientation, this tells us what it is. If not, it just tells us what orientation to use.
 /// </summary>
 /// <returns>
 /// <c>int</c> The orientation index.
 /// </returns>
 /// <param name='pEntity'>
 /// <see cref="Crystallography.AbstractCrystallonEntity"/>
 /// </param>
 private int GetOrientationIndex( AbstractCrystallonEntity pEntity )
 {
     if (!AppMain.ORIENTATION_MATTERS) {
         return population;
     }
     if ( pEntity is GroupCrystallonEntity ) {
         if ( (pEntity as GroupCrystallonEntity).complete) {
             return population;
         }
     }
     int orientation = ( pEntity as SpriteTileCrystallonEntity ).getOrientation();
     int attachPosition = -1;
     attachPosition = orientation;
     return attachPosition;
 }
 public override void PostAttach( AbstractCrystallonEntity pEntity )
 {
     pEntity.BeSelected( InputManager.MAX_PRESS_DURATION );
     EaseOut();
     selectionDelay = MAX_SELECTION_DELAY;
 }
 /// <summary>
 /// Release an entity from the Group.
 /// </summary>
 public virtual AbstractCrystallonEntity Release( AbstractCrystallonEntity e, bool pForceBreak = false)
 {
     if ( e is SpriteTileCrystallonEntity ) {
         return ReleaseSingle (e as SpriteTileCrystallonEntity );
     } else if ( e is CubeCrystallonEntity ) {
         return ReleaseSingle( e as CubeCrystallonEntity );
     } else {
         return null;
     }
 }
        /// <summary>
        /// Actually reparent a single entity to this group.
        /// </summary>
        /// <param name='pEntity'>
        /// <see cref="Crystallography.AbstractCrystallonEntity"/>
        /// </param>
        public void Attach( AbstractCrystallonEntity pEntity )
        {
            IncreaseSlots( 1 );
            members[GetFreeSlot()] = pEntity;

            //create a child-node, give it a positional offset, make pEntity a child of child-node
            int index = GetOrientationIndex( pEntity );
            if ( _pucks[index] == null ) {
                AddPuck(index);
            }
            Node puck = _pucks[index];
            pEntity.attachTo(puck);
            pEntity.getNode().Position *= 0;
            if ( !AppMain.ORIENTATION_MATTERS ) {
                if ((pEntity is CubeCrystallonEntity) == false ) {
                    QualityManager.Instance.SetQuality( pEntity, "QOrientation", index );
                }
            }
            _numMembers++;
        }
 // METHODS ---------------------------------------------------------------------------------------------------
 /// <summary>
 /// Add an entity to <c>qualityDict</c>
 /// </summary>
 /// <param name='pEntity'>
 /// <see cref="Crystallography.AbstractCrystallonEntity"/>
 /// </param>
 /// <param name='pQualityName'>
 /// <c>string</c> Class name for this quality.
 /// </param>
 /// <param name='pVariant'>
 /// <c>int</c> Index of the variant. Probs 0, 1, or 2.
 /// </param>
 private void Add( AbstractCrystallonEntity pEntity, string pQualityName, int pVariant )
 {
     if (qualityDict[pQualityName][pVariant] == null) {
         qualityDict[pQualityName][pVariant] = new List<int>();
     }
     qualityDict[pQualityName][pVariant].Add( pEntity.id );
 }
        /// <summary>
        /// Handles <c>InputManager.TouchDownDetected</c>.
        /// </summary>
        /// <param name='sender'>
        /// InputManager instance
        /// </param>
        /// <param name='e'>
        /// <see cref="Crystallography.InputManager.BaseTouchEventArgs"/>
        /// </param>
        void HandleInputManagerInstanceTouchDownDetected(object sender, SustainedTouchEventArgs e)
        {
            // NO TOUCHING PIECES WHEN GAME IS PAUSED
            if ( GameScene.paused ) return;
            // NO TOUCHING PIECES WHEN LEVEL IS OVER
            if ( GameScene.Hud.MetGoalTime != 0.0f) return;

            setPosition( e.touchPosition );

            if (velocity < MAXIMUM_PICKUP_VELOCITY) {
                if ( InputManager.dragging ) { // ----------------------------------- LOOK FOR ENTITIES THE PLAYER MIGHT BE TRYING TO TOUCH
                    // TEST FINGER POSITION
                    var entity = GetEntityAtPosition( e.touchPosition );
                    if (justDownPositionEntity != null) { // ------------------------ EDGE CASE: PLAYER TOUCHED DOWN ON A PIECE, BUT DRAGGED OFF OF IT
                        if (justDownPositionEntity != entity ) { // ----------------- BEFORE WE ADDED IT TO THE SELECTION GROUP.
                            if (entity == null) {
                                entity = justDownPositionEntity; // ----------------- COMMON: PLAYER IS TOUCHING EMPTY SPACE; RESOLVE IT BELOW
                            }
                        }
                        justDownPositionEntity = null;
                    }

                    if ( entity == null
                         && velocity < 100.0f
                         && population != 0) {
                        // TRY THE PICK UP POSITIONS FOR THE PIECE THE PLAYER ALREADY HAS

                        if (_pucks[(int)POSITIONS.TOP].Children.Count != 0) {
                            entity = GetEntityAtPosition( getNode().LocalToWorld(UP_LEFT_SELECTION_POINT), POSITIONS.LEFT);
                            if (entity == null) {
                                entity = GetEntityAtPosition( getNode().LocalToWorld(UP_RIGHT_SELECTION_POINT), POSITIONS.RIGHT);
                            }
                        }
                        if ( entity == null && _pucks[(int)POSITIONS.LEFT].Children.Count != 0) {
                            entity = GetEntityAtPosition( getNode().LocalToWorld(LEFT_UP_SELECTION_POINT), POSITIONS.TOP);
                        }
                        if ( entity == null && _pucks[(int)POSITIONS.RIGHT].Children.Count != 0){
                            entity = GetEntityAtPosition( getNode().LocalToWorld(RIGHT_UP_SELECTION_POINT), POSITIONS.TOP);
                        }
                    }
                    if (entity != null) {
                        if ( selectionDelay == 0.0f) {
                            Add (entity);
                        }
                    }
                    lastEntityTouched = entity;
                }
            }
        }
        /// <summary>
        /// Handles <c>InputManager.TouchJustDownDetected</c>.
        /// </summary>
        /// <param name='sender'>
        /// Input Manager instance
        /// </param>
        /// <param name='e'>
        /// <see cref="Crystallography.InputManager.BaseTouchEventArgs"/>
        /// </param>
        void HandleInputManagerInstanceTouchJustDownDetected(object sender, BaseTouchEventArgs e)
        {
            if ( GameScene.paused ) return;
            if ( GameScene.Hud.MetGoalTime != 0.0f) return;
            setPosition( e.touchPosition );

            lastEntityTouched = justDownPositionEntity = GetEntityAtPosition( e.touchPosition ) as AbstractCrystallonEntity;

            AddParticle(0.0f);
            Scheduler.Instance.Schedule(this.getNode(), AddParticle, 0.1f, false, 1);

            if ( lastEntityTouched is CardCrystallonEntity ) {
                Add ( lastEntityTouched );
                justDownPositionEntity = null;
            }
        }
        /// <summary>
        /// Reset the SelectionGroup
        /// </summary>
        /// <param name='pScene'>
        /// Instance of the scene to be a part of after resetting.
        /// </param>
        public void Reset( Scene pScene )
        {
            RemoveAll();
            MemberType = null;
            if (lastPosition != null) {
                lastPosition.Clear();
            } else {
                lastPosition = new List<Vector2>();
            }
            if (selectionPoints != null) {
                selectionPoints.Clear();
            } else {
                selectionPoints = new List<Vector2>();
            }
            lastPosition.Add( getPosition() );
            heading = Vector2.Zero;
            lastEntityReleased = null;
            lastEntityTouched = null;
            justDownPositionEntity = null;
            _scene = pScene;
            easeState = EaseState.IN;
            selectionDelay = 0.0f;

            // NEED TO RESET THESE IN CASE PLAYER ADJUSTED EASE DISTANCE IN OPTIONS MENU
            UP_OFFSET = new Vector2(0.5f, 30.0f+EASE_DISTANCE);
            LEFT_OFFSET = new Vector2(-EASE_DISTANCE-35.0f, 17.5f);
            RIGHT_OFFSET = new Vector2(EASE_DISTANCE+35.0f, 17.5f);
            UP_LEFT_SELECTION_POINT = new Vector2(-39.5f, 10.0f+EASE_DISTANCE);
            UP_RIGHT_SELECTION_POINT = new Vector2(40.5f, 10.0f+EASE_DISTANCE);
            LEFT_UP_SELECTION_POINT = new Vector2(-EASE_DISTANCE-20, 55.5f);
            RIGHT_UP_SELECTION_POINT = new Vector2(EASE_DISTANCE+20, 55.5f);
        }
 /// <summary>
 /// Release the currently selected ICrystallonEntity from the SelectionGroup.
 /// </summary>
 //        public ICrystallonEntity Release () {
 public override AbstractCrystallonEntity Release( AbstractCrystallonEntity e, bool pForceBreak = false )
 {
     AbstractCrystallonEntity entity = e;
     if (pForceBreak) {
         lastEntityReleased = null;
         if (entity is GroupCrystallonEntity) {
             this.Break ();
             return entity;
         }
         return ReleaseSingle ( entity );
     } else if (population == 1) {
         entity = members[0];
         return lastEntityReleased = ReleaseSingle ( entity );
     }
     bool isComplete = false;
     if ( !pForceBreak ) {	// --------------------------- don't bother testing completeness if Break forced
         if ( population  == MAX_CAPACITY ) { // -------------------------------------------------------- EVALUATE CUBES!
             if (QualityManager.Instance.CheckForMatch( this, true ) ) {
                 GroupComplete();
                 isComplete = true;
             } else {
                 GroupFailed();
                 return null;
             }
         }
     }
     if ( entity is NodeCrystallonEntity ) {
         return lastEntityReleased = ReleaseGroup ( isComplete, pForceBreak );
     }
     return lastEntityReleased = null;
 }
 public void Pull(AbstractCrystallonEntity pEntity)
 {
     var dir = getPosition() - pEntity.getPosition();
     pEntity.addImpulse(0.03f * dir.Normalize() * GamePhysics.PtoM );
 }
 /// <summary>
 /// Removes an entity from all qualities in <c>qualityDict</c>
 /// </summary>
 /// <param name='pEntity'>
 /// The Entity
 /// </param>
 public void RemoveAll( AbstractCrystallonEntity pEntity )
 {
     var allQualities = qualityDict.Keys.ToList();
     foreach( var quality in allQualities ) {
         Remove (pEntity, quality);
     }
 }
 public bool CheckMemberTypeCompatability( AbstractCrystallonEntity pEntity )
 {
     // NO CURRENT MEMBER TYPE
     if (MemberType == null) {
         if ( typeof(GroupCrystallonEntity).IsAssignableFrom(pEntity.GetType()) ) { // ------- HANDLE GROUP-TYPE OBJECTS
             MemberType = (pEntity as GroupCrystallonEntity).MemberType;
         } else if ( typeof(CardCrystallonEntity).IsAssignableFrom(pEntity.GetType()) ) { // - HANDLE CARD-TYPE OBJECTS
             MemberType = typeof(CardCrystallonEntity);
         }
     } else {
         Type t = null;
         if ( typeof(GroupCrystallonEntity).IsAssignableFrom(pEntity.GetType()) ) { // ------- HANDLE GROUP-TYPE OBJECTS
             t = (pEntity as GroupCrystallonEntity).MemberType;
         } else if ( typeof(CardCrystallonEntity).IsAssignableFrom(pEntity.GetType()) ) { // - HANDLE CARD-TYPE OBJECTS
             t = typeof(CardCrystallonEntity);
         }
         return this.MemberType.IsAssignableFrom(t);
     }
     return true;
 }
 /// <summary>
 /// Apply a particular variant of a particular quality to an entity. Also removes a previous variant of that quality, if it exists.
 /// </summary>
 /// <param name='pEntity'>
 /// <see cref="Crystallography.AbstractCrystallonEntity"/>
 /// </param>
 /// <param name='pQualityName'>
 /// <c>string</c> Class name for this quality. Must start with a capitol Q.
 /// </param>
 /// <param name='pVariant'>
 /// <c>int</c> Index of the variant. Probs 0, 1, or 2.
 /// </param>
 public void SetQuality( AbstractCrystallonEntity pEntity, string pQualityName, int pVariant )
 {
     var type = Type.GetType( "Crystallography." + pQualityName );
     var quality = (IQuality)type.GetProperty("Instance").GetValue(null, null);
     Remove( pEntity, pQualityName );
     Add( pEntity, pQualityName, pVariant );
     quality.Apply( pEntity, pVariant );
 }
 /// <summary>
 /// This is primarily used for positioning the group's pucks
 /// </summary>
 /// <param name='pEntity'>
 /// P entity.
 /// </param>
 public virtual void PostAttach( AbstractCrystallonEntity pEntity )
 {
     if ( population  > 1 ) {
         foreach( AbstractCrystallonEntity e in members ) {
             if( e != null) {
                 int puckIndex = Array.IndexOf(_pucks, e.getNode().Parent);
                 _pucks[puckIndex].Position = e.getAttachOffset( puckIndex );
             }
         }
     } else { // JUST CENTER THE OBJECT IF THERE'S ONLY ONE.
         foreach (var puck in _pucks) {
             puck.Position *= 0;
         }
     }
 }
 /// <summary>
 /// Remove an entity from <c>qualityDict</c>
 /// </summary>
 /// <param name='pEntity'>
 /// <see cref="Crystallography.AbstractCrystallonEntity"/>
 /// </param>
 /// <param name='pQualityName'>
 /// <c>string</c> Class name for this quality.
 /// </param>
 private void Remove( AbstractCrystallonEntity pEntity, string pQualityName )
 {
     foreach ( List<int> list in qualityDict[pQualityName] ) {
         if ( list == null ) {
             continue;
         }
         if( list.Remove(pEntity.id) ) {
             return;
         }
     }
 }
        public void Destroy()
        {
            InputManager.Instance.DoubleTapDetected -= HandleInputManagerInstanceDoubleTapDetected;
            InputManager.Instance.TouchJustDownDetected -= HandleInputManagerInstanceTouchJustDownDetected;
            InputManager.Instance.TouchJustUpDetected -= HandleInputManagerInstanceTouchJustUpDetected;
            InputManager.Instance.TouchDownDetected -= HandleInputManagerInstanceTouchDownDetected;
            InputManager.Instance.DragDetected -= HandleInputManagerInstanceDragDetected;
            InputManager.Instance.TapDetected -= HandleInputManagerInstanceTapDetected;

            RemoveAll();
            this.removeFromScene(true);
            _instance = null;
            lastPosition.Clear();
            lastPosition = null;
            _scene = null;
            lastEntityReleased = null;
            lastEntityTouched = null;
            justDownPositionEntity = null;
        }