Пример #1
0
        public override void ComposeSensorTargetSet(GameActor gameActor, Reflex reflex)
        {
            List <Filter> filters = reflex.Filters;

            // add normal sightSet of items to the targetset
            //
            senseSetIter.Reset();
            while (senseSetIter.MoveNext())
            {
                SensorTarget target = (SensorTarget)senseSetIter.Current;

                bool match = true;
                bool cursorFilterPresent = false;
                for (int indexFilter = 0; indexFilter < filters.Count; indexFilter++)
                {
                    Filter filter = filters[indexFilter] as Filter;
                    ClassificationFilter cursorFilter = filter as ClassificationFilter;
                    if (cursorFilter != null && cursorFilter.classification.IsCursor)
                    {
                        cursorFilterPresent = true;
                    }

                    if (!filter.MatchTarget(reflex, target))
                    {
                        match = false;
                        break;
                    }
                }
                if (match)
                {
                    if (!target.Classification.IsCursor || cursorFilterPresent)
                    {
                        reflex.targetSet.Add(target);
                    }
                }
            }

            if (ListeningForMusic(filters))
            {
                if (HearMusic(filters))
                {
                    SensorTarget sensorTarget = SensorTargetSpares.Alloc();
                    sensorTarget.Init(gameActor, Vector3.UnitZ, 0.0f);
                    reflex.targetSet.AddOrFree(sensorTarget);
                }
            }

            reflex.targetSet.Action = TestObjectSet(reflex);
            if (reflex.targetSet.Action)
            {
                foreach (SensorTarget targ in reflex.targetSet)
                {
                    gameActor.AddSoundLine(targ.GameThing);
                }
            }
        }
Пример #2
0
 public override void FinishUpdate(GameActor gameActor)
 {
     if (gameActor.ThingBeingHeldByThisActor != null)
     {
         // presence is only thing important here
         SensorTarget target = SensorTargetSpares.Alloc();
         target.Init(gameActor, gameActor.ThingBeingHeldByThisActor);
         senseSet.AddOrFree(target);
     }
 }
Пример #3
0
        }     // end of SortTargets()

        private void ClearTargets()
        {
            foreach (SensorTarget target in sensorTargets.Values)
            {
                // The last unref must be done by the spare list.
                if (target.UnRef() == 1)
                {
                    SensorTargetSpares.Free(target);
                }
            }
            sensorTargets.Clear();
            dirty = true;
        }
Пример #4
0
 /// <summary>
 /// Try adding to the set, if unsuccessful, free the SensorTarget.
 /// This is a shortcut for when you've just Alloc'd a sensor target,
 /// and then:
 ///   if(!Add(targ))
 ///     SensorTargetSpares.Release(targ);
 /// This is only appropriate where the target has just been alloc'd, so
 /// not iterating through a set, and is a local that will just fall out of scope.
 /// </summary>
 /// <param name="sensorTarget"></param>
 /// <returns>false if not added but freed, true if added</returns>
 public bool AddOrFree(SensorTarget sensorTarget)
 {
     Debug.Assert(sensorTarget.refs == 1, "Improper usage, use Add()?");
     if (!Add(sensorTarget))
     {
         if (sensorTarget.refs == 1)
         {
             SensorTargetSpares.Free(sensorTarget);
         }
         return(false);
     }
     return(true);
 }
Пример #5
0
        public void Add(GameThing gameThing, Vector3 direction, float range)
        {
            if (sensorTargets.ContainsKey(gameThing.UniqueNum))
            {
                return;
            }

            SensorTarget target = SensorTargetSpares.Alloc();

            target.Init(gameThing, direction, range);
            sensorTargets.Add(gameThing.UniqueNum, target.Ref());
            dirty = true;
        }
Пример #6
0
        public override void FinishUpdate(GameActor gameActor)
        {
            if (terrainSensor != null)
            {
                //if (TouchEdit.HitInfo.TerrainHit)
                {
                    terrainSensor.OverrideSenseMaterial = TouchEdit.HitInfo.TerrainMaterial;
                }

                terrainSensor.FinishUpdate(gameActor);
            }
            else
            {
                if (TouchEdit.HitInfo.HaveActor)
                {
                    GameActor touchdActor = TouchEdit.HitInfo.ActorHit;

                    Vector3 actorCenter = Vector3.Transform(
                        gameActor.BoundingSphere.Center,
                        gameActor.Movement.LocalMatrix);

                    Vector3 thingCenter = Vector3.Transform(
                        touchdActor.BoundingSphere.Center,
                        touchdActor.Movement.LocalMatrix);

                    Vector3 direction = thingCenter - actorCenter;

                    float range = direction.Length();
                    if (range > 0.0f)
                    {
                        direction *= 1.0f / range; // Normalize.
                    }

                    SensorTarget target = SensorTargetSpares.Alloc();
                    target.Init(touchdActor, direction, range);
                    senseSet.AddOrFree(target);
                }

                senseSet.Add(NullActor.Instance, Vector3.Zero, float.MaxValue);
            }
        }
Пример #7
0
        public override void ComposeSensorTargetSet(GameActor gameActor, Reflex reflex)
        {
            if (terrainSensor != null)
            {
                terrainSensor.ComposeSensorTargetSet(gameActor, reflex);
            }
            else
            {
                List <Filter> filters = reflex.Filters;

                // The sensor will use the first TouchGestureFilter it finds. Since any given reflex
                // can only have a single one of these, this works well.
                TouchGestureFilter gestureFilter   = null;
                TouchButtonFilter  buttonFilter    = null;
                GUIButtonFilter    guiButtonFilter = null;

                for (int i = 0; i < filters.Count; i++)
                {
                    if (filters[i] is TouchGestureFilter)
                    {
                        Debug.Assert(null == gestureFilter);
                        gestureFilter = filters[i] as TouchGestureFilter;
                    }
                    if (filters[i] is TouchButtonFilter)
                    {
                        Debug.Assert(null == buttonFilter);
                        buttonFilter = filters[i] as TouchButtonFilter;
                    }
                    if (filters[i] is GUIButtonFilter)
                    {
                        Debug.Assert(null == guiButtonFilter);
                        guiButtonFilter = filters[i] as GUIButtonFilter;
                    }
                }

                //if we don't have a filter, do nothing
                if (null == gestureFilter &&
                    null == buttonFilter &&
                    null == guiButtonFilter)
                {
                    //Debug.Assert(false, "Did not find a touch filter for touch sensor.");
                    return;
                }

                // Cause the filter to detect what is under the touch cursor and set that information on the reflex.
                // MatchAction will be true if the TouchFilter passes but then we still need to filter on any other
                // filters including the ever annoying "not", "me" and "anything" filters.
                Object param       = null;
                bool   matchAction = false;

                bool notFilter      = reflex.Data.FilterExists("filter.not");
                bool anythingFilter = reflex.Data.FilterExists("filter.anything");

                if (gestureFilter != null)
                {
                    matchAction = gestureFilter.MatchAction(reflex, out param);
                }

                if (buttonFilter != null)
                {
                    // Make the actual on screen button visible if a reflex contains the touch button filter.
                    //TouchButtons.MakeVisible(buttonFilter.button);

                    matchAction = buttonFilter.MatchAction(reflex, out param);
                }

                if (guiButtonFilter != null)
                {
                    matchAction = guiButtonFilter.MatchAction(reflex, out param);
                }



                // Give the anythingFilter a chance to kill the action.
                if (anythingFilter && reflex.TouchActor == null)
                {
                    matchAction = false;
                }

                // Handle the "Not" filter
                if (notFilter)
                {
                    if (matchAction)
                    {
                        matchAction          = false;
                        reflex.TouchActor    = null;
                        reflex.TouchPosition = null;
                    }
                    else
                    {
                        matchAction = true;
                    }
                }

                bool touchCursorTarget = false;

                // We are moving towards a remembered target position if we're the movement actuator.
                // We do not require a match on the current frame to continue the movement.
                // Note, in the presence of a "Not" filter, there is no target to "not move" to, so this
                // is negated.
                // Also, support turning toward a remembered position.
                bool movingToTouchPos = (reflex.actuatorUpid == "actuator.movement" && !notFilter);
                movingToTouchPos |= (reflex.actuatorUpid == "actuator.turn" && !notFilter);

                if (movingToTouchPos && (reflex.TouchActor == reflex.Task.GameActor))
                {
                    // When moving with touch, going towards oneself doesn't make sense. By discarding
                    // the touch actor and using the terrain position, we have a better definition of this
                    // movement. This allows for small positional corrections around the target's bounding
                    // box as well as letting slide movements that begin on oneself to work.
                    reflex.TouchActor    = null;
                    reflex.TouchPosition = TouchEdit.HitInfo.TerrainPosition;
                }

                if (matchAction || movingToTouchPos)
                {
                    bool         validTarget = false;
                    SensorTarget target      = SensorTargetSpares.Alloc();
                    // If we have an actor touch target, check if it passes the filters.
                    // If not, null it out.
                    if (reflex.TouchActor != null)
                    {
                        // Create a target.
                        target.Init(reflex.Task.GameActor, reflex.TouchActor);
                        validTarget = true;
                    }
                    else
                    {
                        target.Init(reflex.Task.GameActor, reflex.Task.GameActor);
                        // If we've got a me filter and not TouchActor we've failed.
                        if (reflex.Data.FilterExists("filter.me"))
                        {
                            matchAction = false;
                        }

                        // NOTE: For classification purposes, if the user doesn't touch a particular actor,
                        // we create an 'empty' classification. This is because we need a classification object
                        // in order to do reflexes such as 'tap move blimp -> shoot'. If you don't tap an actor,
                        // it needs to compare the absence of an actor to the blimp.
                        if (reflex.TouchPosition != null)
                        {
                            // We need to add the touch position to the targetSet.
                            target.Classification = new Classification();
                            target.GameThing      = senseSet.Nearest.GameThing;
                            target.Position       = reflex.TouchPosition.Value;
                            target.Direction      = target.Position - reflex.Task.GameActor.Movement.Position;
                            target.Range          = target.Direction.Length();

                            // If we've arrived at the target position, clear MousePosition.
                            Vector2 dir2d = new Vector2(target.Direction.X, target.Direction.Y);
                            if (dir2d.LengthSquared() < 0.1f)
                            {
                                reflex.TouchPosition = null;
                            }

                            target.Direction /= target.Range;
                            touchCursorTarget = true;
                            validTarget       = true;
                        }
                        else
                        {
                            // We don't have an actor or a position. Only with an action match should we
                            // create a sensor target.
                            if (matchAction)
                            {
                                // Calc a fake position for this target.
                                GameActor actor = reflex.Task.GameActor;
                                Vector3   pos   = actor.Movement.Position;

                                target.Classification = new Classification();
                                target.GameThing      = senseSet.Nearest.GameThing;
                                target.Position       = pos;
                                target.Direction      = target.Position - reflex.Task.GameActor.Movement.Position;
                                target.Range          = target.Direction.Length();
                                target.Direction     /= target.Range;
                                validTarget           = true;
                            }
                        }
                    }

                    // Test the target against the filters.
                    if (validTarget)
                    {
                        for (int i = 0; i < filters.Count; i++)
                        {
                            if (!(filters[i] is TouchGestureFilter))
                            {
                                if (!filters[i].MatchTarget(reflex, target) ||
                                    !filters[i].MatchAction(reflex, out param))
                                {
                                    if (!notFilter)
                                    {
                                        // Failed a match on one of the filters and we don't have a negation
                                        // so null things out and short circuit.
                                        reflex.TouchActor = null;
                                        validTarget       = false;
                                        matchAction       = false;
                                        touchCursorTarget = false;
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    // If we still have a target, add it to the target set.
                    if (validTarget)
                    {
                        reflex.targetSet.Add(target);
                    }
                    else
                    {
                        // Don't really need this target so free it.
                        SensorTargetSpares.Free(target);
                    }
                }

                reflex.targetSet.Action = reflex.targetSet.Count > 0; // && TestObjectSet(reflex);

                //reflex.targetSet.Action |= matchAction;

                // This forces the movement to keep going toward the clicked position/bot
                // even if the touch is no longer clicked.
                reflex.targetSet.ActionMouseTarget = touchCursorTarget;

                if (matchAction)
                {
                    bool postProcessCheck = PostProcessAction(matchAction, reflex);
                    if (!postProcessCheck)
                    {
                        // We matched the action, but it failed the post process check (used by "Once" modifier),
                        // so we clear the target set.
                        reflex.targetSet.Clear();
                    }
                }
            }
        }
Пример #8
0
        public override void ComposeSensorTargetSet(GameActor gameActor, Reflex reflex)
        {
            if (terrainSensor != null)
            {
                terrainSensor.ComposeSensorTargetSet(gameActor, reflex);
            }
            else
            {
                List <Filter> filters = reflex.Filters;

                MouseFilter mouseFilter = reflex.Data.GetFilterByType(typeof(MouseFilter)) as MouseFilter;

                if (mouseFilter == null)
                {
                    // TODO this should trigger a hint.  You should always have a mouse filter with a mouse sensor.
                    return;
                }

                // Cause the filter to detect what is under the mouse cursor and set that information on the reflex.
                // MatchAction will be true if the MouseFilter passes but then we still need to filter on any other
                // filters including the ever annoying "not", "me" and "anything" filters.
                Object param       = null;
                bool   matchAction = mouseFilter.MatchAction(reflex, out param);

                //-----------------------------
                //GUI Button filter.
                GUIButtonFilter guiButtonFilter = reflex.Data.GetFilterByType(typeof(GUIButtonFilter)) as GUIButtonFilter;
                if (null != guiButtonFilter)
                {
                    matchAction = guiButtonFilter.MatchAction(reflex, out param);
                }

                bool notFilter      = reflex.Data.FilterExists("filter.not");
                bool anythingFilter = reflex.Data.FilterExists("filter.anything");

                // Give the anythingFilter a chance to kill the action.
                if (anythingFilter && reflex.MouseActor == null)
                {
                    matchAction = false;
                }

                bool mouseCursorTarget = false;
                bool mouseTargetNeeded = false;
                if (matchAction || reflex.IsMovement)
                {
                    GameActor mouseActor = reflex.MouseActor;   // Preserve this in case we null out the reflex ref and then restore it because of "not".  Argh.

                    // If we have an actor mouse target, check if it passes the filters.
                    // If not, null it out.
                    if (reflex.MouseActor != null)
                    {
                        // Create a target.
                        SensorTarget target = SensorTargetSpares.Alloc();
                        target.Init(reflex.Task.GameActor, reflex.MouseActor);

                        // Test againt the filters.
                        for (int i = 0; i < filters.Count; i++)
                        {
                            if (!filters[i].MatchTarget(reflex, target))
                            {
                                reflex.MouseActor = null;
                                matchAction       = false;
                            }
                        }

                        // Handle "not" filter.
                        if (notFilter)
                        {
                            if (reflex.MouseActor == null)
                            {
                                // Restore MouseActor.
                                reflex.MouseActor = mouseActor;
                            }
                            else
                            {
                                // Killed by the "not" filter.
                                reflex.MouseActor    = null;
                                reflex.MousePosition = null;
                                matchAction          = false;
                            }
                        }

                        // If we stil have a target, add it to the target set.
                        if (reflex.MouseActor != null)
                        {
                            reflex.targetSet.Add(target);
                            mouseCursorTarget = true;
                        }
                        else
                        {
                            // Don't really need this target so free it.
                            SensorTargetSpares.Free(target);
                        }
                    }
                    else
                    {
                        // No MouseActor case (the mouse click was not on an actor)

                        // If we have any classification filters, then we must have been
                        // looking for an actor. Since we don't have an actor, the
                        // result is false.
                        if (reflex.hasClassificationFitler)
                        {
                            matchAction = false;
                        }

                        // If we've got a me filter and not MouseActor we've failed.
                        if (reflex.hasMeFilter)
                        {
                            matchAction = false;
                        }

                        // MouseActor is null so we didn't click on anything which means that if we have an anything
                        // filter, matchAction should stay false.  Unless, of course we also have a notFilter.
                        if (!anythingFilter || notFilter)
                        {
                            // No mouse actor but if we have a "not" or this is a movement toward reflex then the result should be true.
                            // The WHEN Mouse Over DO Turn Toward option is used for mouse-look style controls.
                            // Note that if the reflex is Move Forward we don't want this to trigger.
                            if (notFilter || (reflex.IsMovement && reflex.selectorUpid == "selector.towardclosest") || (reflex.IsMovement && reflex.actuatorUpid == "actuator.turn"))
                            {
                                mouseTargetNeeded = true;
                                matchAction       = true;
                            }
                        }
                    }
                }
                else
                {
                    if (notFilter || reflex.IsMovement)
                    {
                        mouseTargetNeeded = true;
                    }
                }

                // If MouseActor is null, we may still want to target a position on the ground.
                //if (reflex.MouseActor == null && !matchAction && reflex.IsMovement)
                if (mouseTargetNeeded || (reflex.MouseActor == null && matchAction))
                {
                    if (reflex.MousePosition != null)
                    {
                        // We need to add the mouse position to the targetSet.
                        SensorTarget target = SensorTargetSpares.Alloc();
                        target.GameThing = senseSet.Nearest != null ? senseSet.Nearest.GameThing : NullActor.Instance;
                        target.Position  = reflex.MousePosition.Value;
                        target.Direction = target.Position - reflex.Task.GameActor.Movement.Position;
                        target.Range     = target.Direction.Length();

                        // If we've arrived at the target position, clear MousePosition.
                        Vector2 dir2d = new Vector2(target.Direction.X, target.Direction.Y);
                        if (dir2d.LengthSquared() < 0.1f)
                        {
                            reflex.MousePosition = null;
                        }

                        target.Direction /= target.Range;
                        reflex.targetSet.Add(target);
                        mouseCursorTarget = true;
                    }
                    else
                    {
                        // Is this a mouse move case?  If so translate the param values into
                        // a target the actuator can use.
                        MouseFilter filter = (MouseFilter)reflex.Data.GetFilterByType(typeof(MouseFilter));
                        if (filter.type == MouseFilterType.Move)
                        {
                            Vector2 position = (Vector2)param;

                            SensorTarget target = SensorTargetSpares.Alloc();

                            target.GameThing = senseSet.Nearest.GameThing;
                            // Calc fake position.
                            GameActor actor = reflex.Task.GameActor;
                            Vector3   pos   = actor.Movement.Position;

                            target.Position  = pos;
                            target.Direction = target.Position - reflex.Task.GameActor.Movement.Position;
                            target.Range     = target.Direction.Length();
                            if (target.Range == 0)
                            {
                                target.Direction = Vector3.Zero;
                            }
                            else
                            {
                                target.Direction /= target.Range;
                            }
                            reflex.targetSet.Add(target);
                            reflex.targetSet.Param = position;
                            mouseCursorTarget      = true;
                        }
                    }
                }

                reflex.targetSet.Action  = reflex.targetSet.Count > 0 && TestObjectSet(reflex);
                reflex.targetSet.Action |= matchAction;

                // This forces the movement to keep going toward the clicked position/bot
                // even if the mouse is no longer clicked.
                reflex.targetSet.ActionMouseTarget = mouseCursorTarget;

                reflex.targetSet.Action = PostProcessAction(reflex.targetSet.Action, reflex);
            }
        }
Пример #9
0
        public void UpdateSensors(BrainCategories category)
        {
            UpdateUserControlled();
            UpdateIsTurning();
            UpdateMouseButtonPresence();

            if (IsLeftMouseButtonPresent)
            {
                MouseEdit.DisableLeftDrag();
            }
            if (IsRightMouseButtonPresent)
            {
                MouseEdit.DisableRightOrbit();
            }

            for (int i = 0; i < reflexes.Count; i++)
            {
                Reflex reflex = reflexes[i] as Reflex;
                reflex.targetSet.Clear();
            }

            if (reflexes.Count > 0)
            {
                UpdateGameThingSensors(category);

                for (int indentationLevel = 0; indentationLevel <= maxReflexIndentation; ++indentationLevel)
                {
                    // After evaluating root-level reflexes, open up to all sensor categories.
                    if (indentationLevel > 0)
                    {
                        category = BrainCategories.NotSpecified;
                    }

                    for (int i = 0; i < reflexes.Count; i++)
                    {
                        Reflex reflex = reflexes[i];

                        // Only evaluate reflexes at the current level of indentation.
                        if (reflex.Indentation != indentationLevel)
                        {
                            continue;
                        }

                        // Only evaluate sub-reflexes of parents that evaluated true except
                        // in the case where the reflex actuator is movement based.
                        if (reflex.Parent != null && !reflex.Parent.targetSet.Action)
                        {
                            // Nested reflexes with once modifiers need to clear their "once" state.
                            reflex.ResetOnceModifiers();

                            // Nested reflexes with active mouse targets should still be acted on
                            // so create valid target and action sets for them.  Note that this
                            // only applies to reflexes with a movement actuator.  This is so you
                            // can have WHEN MouseLeft DO Move and the result will be that the
                            // bot continues to move toward the target (position or bot) even
                            // after the parent reflex goes false.
                            bool mouseTarget = reflex.MousePosition != null || reflex.MouseActor != null;
                            if (mouseTarget && reflex.IsMovement)
                            {
                                // Create a sensor target set
                                if (reflex.MouseActor != null)
                                {
                                    // We need to add the mouse actor to the targetSet.
                                    SensorTarget target = SensorTargetSpares.Alloc();
                                    target.Init(reflex.Task.GameActor, reflex.MouseActor);
                                    reflex.targetSet.Add(target);
                                }
                                else if (reflex.MousePosition != null)
                                {
                                    // We need to add the mouse position to the targetSet.
                                    SensorTarget target = SensorTargetSpares.Alloc();
                                    target.GameThing  = GameActor;
                                    target.Position   = reflex.MousePosition.Value;
                                    target.Direction  = target.Position - reflex.Task.GameActor.Movement.Position;
                                    target.Range      = target.Direction.Length();
                                    target.Direction /= target.Range;
                                    reflex.targetSet.Add(target);
                                }
                                reflex.targetSet.ActionMouseTarget = true;
                                reflex.CreateActionSet(GameActor, 0);
                            }

                            continue;
                        }

                        // Don't evaluate reflexes that don't match the given categories, if any were specified.
                        if ((category != BrainCategories.NotSpecified) && (reflex.Sensor == null || !reflex.Sensor.Categories.Get((int)category)))
                        {
                            continue;
                        }

                        reflex.Update(GameActor, i);
                    }
                }
            }
        }
        public override void ComposeSensorTargetSet(GameActor gameActor, Reflex reflex)
        {
            List <Filter> filters = reflex.Filters;

            // If we have a "me" filter we need to look at the ShooterHitSet to
            // see if anything hit us.
            if (reflex.Data.HasTile("filter.me"))
            {
                SensorTargetSet.Enumerator shooterSetIter = (SensorTargetSet.Enumerator)gameActor.ShooterHitSet.GetEnumerator();
                shooterSetIter.Reset();
                while (shooterSetIter.MoveNext())
                {
                    SensorTarget target = (SensorTarget)shooterSetIter.Current;

                    // Filtering doesn't make much sense since the "target" is me.  What
                    // I really want to filter on are the characteristics of the shooter.
                    // Which means I need to know the shooter.
                    SensorTarget shooter = SensorTargetSpares.Alloc();
                    shooter.Init(target.Shooter, target.Direction, target.Range);
                    shooter.Tag = target.Tag;

                    bool match = true;
                    for (int indexFilter = 0; indexFilter < filters.Count; indexFilter++)
                    {
                        Filter filter = filters[indexFilter] as Filter;
                        // Ignore me filter since it shouldn't be possible to shoot yourself.
                        if (filter.upid != "filter.me")
                        {
                            if (!filter.MatchTarget(reflex, shooter))
                            {
                                match = false;
                                break;
                            }
                        }
                    }
                    if (match)
                    {
                        reflex.targetSet.Add(target);
                    }
                }
            }
            else
            {
                // add sensorSet of items to the targetset
                senseSetIter.Reset();
                while (senseSetIter.MoveNext())
                {
                    SensorTarget target = (SensorTarget)senseSetIter.Current;

                    bool match = true;
                    for (int indexFilter = 0; indexFilter < filters.Count; indexFilter++)
                    {
                        Filter filter = filters[indexFilter] as Filter;
                        if (!filter.MatchTarget(reflex, target))
                        {
                            match = false;
                            break;
                        }
                    }
                    if (match)
                    {
                        reflex.targetSet.Add(target);

                        // Until we enable payloads on the 'shot hit' reflex, leave the target in the hit set
                        // so that it will have the payload from the shoot reflex applied to it.
                        //gameActor.MissileHitSet.Remove(target.GameThing);

                        MissileHitTargetParam param = target.Tag as MissileHitTargetParam;

                        SetUpShotDamage(reflex, param);
                    }
                }
            }

            reflex.targetSet.Action = TestObjectSet(reflex);
        }