Пример #1
0
    IEnumerator WaitTrafficLight()
    {
        currentStopTime = 0f;
        yield return(FixedUpdateManager.WaitUntilFixed(() => distanceToStopTarget <= stopLineDistance));

        if (prevMapLane.stopLine.currentState == MapData.SignalLightStateType.Green)
        {
            yield break; // light is green so just go
        }

        isStopLight = true;
        yield return(FixedUpdateManager.WaitUntilFixed(() => atStopTarget)); // wait if until reaching stop line

        if ((isRightTurn && prevMapLane.rightLaneReverse == null))
        {
            var waitTime  = RandomGenerator.NextFloat(0f, 3f);
            var startTime = currentStopTime;
            yield return(FixedUpdateManager.WaitUntilFixed(() => prevMapLane.stopLine.currentState == MapData.SignalLightStateType.Green || currentStopTime - startTime >= waitTime));

            isStopLight = false;
            yield break;
        }

        yield return(FixedUpdateManager.WaitUntilFixed(() => prevMapLane.stopLine.currentState == MapData.SignalLightStateType.Green)); // wait until green light

        if (isLeftTurn || isRightTurn)
        {
            yield return(FixedUpdateManager.WaitForFixedSeconds(RandomGenerator.NextFloat(1f, 2f))); // wait to creep out on turn
        }

        isStopLight = false;
    }
Пример #2
0
    protected virtual void SetLaneChange()
    {
        if (currentMapLane == null) // Prevent null if despawned during wait
        {
            return;
        }

        ApiManager.Instance?.AddLaneChange(gameObject);

        if (currentMapLane.leftLaneForward != null)
        {
            if (!isFrontLeftDetect)
            {
                currentMapLane       = currentMapLane.leftLaneForward;
                laneSpeedLimit       = currentMapLane.speedLimit;
                aggressionAdjustRate = laneSpeedLimit / 11.176f; // 11.176 m/s corresponds to 25 mph
                SetChangeLaneData(currentMapLane.mapWorldPositions);
                controller.Coroutines.Add(FixedUpdateManager.StartCoroutine(DelayOffTurnSignals()));
            }
        }
        else if (currentMapLane.rightLaneForward != null)
        {
            if (!isFrontRightDetect)
            {
                currentMapLane       = currentMapLane.rightLaneForward;
                laneSpeedLimit       = currentMapLane.speedLimit;
                aggressionAdjustRate = laneSpeedLimit / 11.176f; // 11.176 m/s corresponds to 25 mph
                SetChangeLaneData(currentMapLane.mapWorldPositions);
                controller.Coroutines.Add(FixedUpdateManager.StartCoroutine(DelayOffTurnSignals()));
            }
        }
    }
Пример #3
0
    protected IEnumerator DelayOffTurnSignals()
    {
        yield return(FixedUpdateManager.WaitForFixedSeconds(3f));

        isLeftTurn = isRightTurn = false;
        controller.SetNPCTurnSignal();
    }
Пример #4
0
 public void ForceLaneChange(bool isLeft)
 {
     if (isLeft)
     {
         if (currentMapLane.leftLaneForward != null)
         {
             if (!isFrontLeftDetect)
             {
                 currentMapLane       = currentMapLane.leftLaneForward;
                 laneSpeedLimit       = currentMapLane.speedLimit;
                 aggressionAdjustRate = laneSpeedLimit / 11.176f; // 11.176 m/s corresponds to 25 mph
                 SetChangeLaneData(currentMapLane.mapWorldPositions);
                 controller.Coroutines.Add(FixedUpdateManager.StartCoroutine(DelayOffTurnSignals()));
                 ApiManager.Instance?.AddLaneChange(gameObject);
             }
         }
     }
     else
     {
         if (currentMapLane.rightLaneForward != null)
         {
             if (!isFrontRightDetect)
             {
                 currentMapLane       = currentMapLane.rightLaneForward;
                 laneSpeedLimit       = currentMapLane.speedLimit;
                 aggressionAdjustRate = laneSpeedLimit / 11.176f; // 11.176 m/s corresponds to 25 mph
                 SetChangeLaneData(currentMapLane.mapWorldPositions);
                 controller.Coroutines.Add(FixedUpdateManager.StartCoroutine(DelayOffTurnSignals()));
                 ApiManager.Instance?.AddLaneChange(gameObject);
             }
         }
     }
 }
Пример #5
0
 private void InitNPC()
 {
     Debug.Assert(LaneData != null);
     rb.isKinematic = true;
     controller.MainCollider.isTrigger = true;
     controller.ResetLights();
     currentSpeed       = 0f;
     rb.angularVelocity = Vector3.zero;
     rb.velocity        = Vector3.zero;
     CurrentIndex       = 0;
     CurrentLoopIndex   = 0;
     CurrentDeactivate  = LaneDeactivate[CurrentIndex];
     if (IdleCoroutine != null)
     {
         FixedUpdateManager.StopCoroutine(IdleCoroutine);
         IdleCoroutine = null;
     }
     if (MoveCoroutine != null)
     {
         FixedUpdateManager.StopCoroutine(MoveCoroutine);
         MoveCoroutine = null;
     }
     if (TriggerCoroutine != null)
     {
         FixedUpdateManager.StopCoroutine(TriggerCoroutine);
         TriggerCoroutine = null;
     }
     WaypointState = WaypointDriveState.Drive;
 }
Пример #6
0
    private void EvaluateLane()
    {
        CurrentIndex++; // index can equal laneData.Count so it can finish npc move IE
        if (CurrentIndex < LaneData.Count)
        {
            CurrentTarget = LaneData[CurrentIndex];
        }

        if (CurrentIndex == LaneData.Count)
        {
            if (WaypointLoop)
            {
                rb.MovePosition(InitPos);
                rb.MoveRotation(InitRot);
                InitNPC();
            }
            else
            {
                WaypointState = WaypointDriveState.Despawn;
                FixedUpdateManager.StopAllCoroutines();
                TriggerCoroutine = null;
                IdleCoroutine    = null;
                MoveCoroutine    = null;
            }
        }
        else
        {
            WaypointState = WaypointDriveState.Drive;
        }
    }
 private void InitPedestrian()
 {
     Debug.Assert(LaneData != null);
     RB.isKinematic = true;
     // TODO currentSpeed = 0f;
     RB.angularVelocity = Vector3.zero;
     RB.velocity        = Vector3.zero;
     CurrentIndex       = 0;
     CurrentLoopIndex   = 0;
     if (IdleCoroutine != null)
     {
         FixedUpdateManager.StopCoroutine(IdleCoroutine);
         IdleCoroutine = null;
     }
     if (MoveCoroutine != null)
     {
         FixedUpdateManager.StopCoroutine(MoveCoroutine);
         MoveCoroutine = null;
     }
     if (TriggerCoroutine != null)
     {
         FixedUpdateManager.StopCoroutine(TriggerCoroutine);
         TriggerCoroutine = null;
     }
     WaypointState = WaypointWalkState.Walk;
 }
Пример #8
0
    protected void GetDodge()
    {
        if (currentMapLane == null)
        {
            return;
        }
        if (isDodge)
        {
            return;
        }
        if (IsYieldToIntersectionLane())
        {
            return;
        }

        if (isLeftDetectWithinStopDistance || isRightDetectWithinStopDistance)
        {
            var npcC = isLeftDetectWithinStopDistance ? leftClosestHitInfo.collider.GetComponentInParent <NPCLaneFollowBehaviour>() : rightClosestHitInfo.collider.GetComponentInParent <NPCLaneFollowBehaviour>();
            var aC   = isLeftDetectWithinStopDistance ? leftClosestHitInfo.collider.transform.root.GetComponent <AgentController>() : rightClosestHitInfo.collider.transform.root.GetComponent <AgentController>();

            if (currentMapLane.isTrafficLane)
            {
                if (npcC != null)
                {
                    isFrontDetectWithinStopDistance = true;
                    frontClosestHitInfo             = isLeftDetectWithinStopDistance ? leftClosestHitInfo : rightClosestHitInfo;
                }
                else if (aC != null)
                {
                    isFrontDetectWithinStopDistance = true;
                    frontClosestHitInfo             = isLeftDetectWithinStopDistance ? leftClosestHitInfo : rightClosestHitInfo;
                    if (!isWaitingToDodge)
                    {
                        controller.Coroutines.Add(FixedUpdateManager.StartCoroutine(WaitToDodge(aC, isLeftDetectWithinStopDistance)));
                    }
                }
                else
                {
                    if (leftClosestHitInfo.collider?.gameObject?.GetComponentInParent <NPCController>() == null && leftClosestHitInfo.collider?.transform.root.GetComponent <AgentController>() == null)
                    {
                        SetDodge(!isLeftDetectWithinStopDistance);
                    }
                }
            }
            else // intersection lane
            {
                if (npcC != null)
                {
                    if ((isLeftTurn && npcC.isLeftTurn || isRightTurn && npcC.isRightTurn) && Vector3.Dot(transform.TransformDirection(Vector3.forward), npcC.transform.TransformDirection(Vector3.forward)) < -0.7f)
                    {
                        if (currentIndex > 1)
                        {
                            SetDodge(isLeftTurn, true);
                        }
                    }
                }
            }
        }
    }
Пример #9
0
    private void EvaluateLane()
    {
        CurrentIndex++; // index can equal laneData.Count so it can finish npc move IE
        if (CurrentIndex < LaneData.Count)
        {
            CurrentTarget            = LaneData[CurrentIndex];
            controller.MovementSpeed = LaneSpeed[CurrentIndex];
            controller.currentSpeed  = LaneSpeed[CurrentIndex];
            controller.steerVector   = (CurrentTarget - controller.frontCenter.position).normalized;
        }

        if (CurrentIndex == LaneData.Count)
        {
            var api = ApiManager.Instance;
            if (WaypointLoop)
            {
                if (CurrentLoopIndex == 0 && api != null)
                {
                    api.AgentTraversedWaypoints(gameObject);
                }
                CurrentLoopIndex++;
                rb.MovePosition(InitPos);
                rb.MoveRotation(InitRot);
                InitNPC();
            }
            else
            {
                if (api != null)
                {
                    api.AgentTraversedWaypoints(gameObject);
                }
                WaypointState = WaypointDriveState.Despawn;
                if (TriggerCoroutine != null)
                {
                    FixedUpdateManager.StopCoroutine(TriggerCoroutine);
                }
                TriggerCoroutine = null;
                if (IdleCoroutine != null)
                {
                    FixedUpdateManager.StopCoroutine(IdleCoroutine);
                }
                IdleCoroutine = null;
                if (MoveCoroutine != null)
                {
                    FixedUpdateManager.StopCoroutine(MoveCoroutine);
                }
                MoveCoroutine = null;
            }
        }
        else
        {
            WaypointState = WaypointDriveState.Drive;
        }
    }
Пример #10
0
 public override void PhysicsUpdate()
 {
     if (WaypointState == WaypointWalkState.Walk)
     {
         if (MoveCoroutine != null)
         {
             return;
         }
         MoveCoroutine = FixedUpdateManager.StartCoroutine(PedestrianMoveIE());
     }
 }
Пример #11
0
 public override void PhysicsUpdate()
 {
     //controller.SetBrakeLights(currentSpeed < 2.0f); // TODO
     if (WaypointState == WaypointDriveState.Drive)
     {
         if (MoveCoroutine != null)
         {
             return;
         }
         MoveCoroutine = FixedUpdateManager.StartCoroutine(NPCMoveIE());
     }
 }
    private void EvaluateSidewalk()
    {
        if (IsRandomIdle())
        {
            controller.Coroutines[(int)PedestrianController.CoroutineID.ChangePedState] =
                FixedUpdateManager.StartCoroutine(controller.ChangePedState());
        }

        if (controller.ThisPedState == PedestrianController.PedestrianState.Walking)
        {
            EvaluateNextTarget();
        }
    }
Пример #13
0
    private IEnumerator NPCIdleIE(float duration, bool deactivate)
    {
        if (deactivate)
        {
            gameObject.SetActive(false);
        }
        yield return(FixedUpdateManager.WaitForFixedSeconds(duration));

        if (deactivate)
        {
            gameObject.SetActive(true);
        }
        EvaluateLane();
        IdleCoroutine = null;
    }
Пример #14
0
    protected IEnumerator DelayChangeLane()
    {
        if (currentMapLane == null)
        {
            yield break;
        }

        if (!currentMapLane.isTrafficLane)
        {
            yield break;
        }

        if (RandomGenerator.Next(100) < 98)
        {
            yield break;
        }

        if (!laneChange)
        {
            yield break;
        }

        if (currentMapLane.leftLaneForward != null)
        {
            isLeftTurn  = true;
            isRightTurn = false;
            controller.SetNPCTurnSignal();
        }
        else if (currentMapLane.rightLaneForward != null)
        {
            isRightTurn = true;
            isLeftTurn  = false;
            controller.SetNPCTurnSignal();
        }

        yield return(FixedUpdateManager.WaitForFixedSeconds(RandomGenerator.NextFloat(1f, 3f)));

        if (currentIndex >= laneData.Count - 2)
        {
            isLeftTurn = isRightTurn = false;
            yield break;
        }

        SetLaneChange();
    }
Пример #15
0
    IEnumerator WaitStopSign()
    {
        yield return(FixedUpdateManager.WaitUntilFixed(() => distanceToStopTarget <= stopLineDistance));

        isStopSign         = true;
        currentStopTime    = 0f;
        hasReachedStopSign = false;
        yield return(FixedUpdateManager.WaitUntilFixed(() => distanceToStopTarget < minTargetDistance));

        prevMapLane.stopLine.intersection.EnterStopSignQueue(controller);
        hasReachedStopSign = true;
        yield return(FixedUpdateManager.WaitForFixedSeconds(stopSignWaitTime));

        yield return(FixedUpdateManager.WaitUntilFixed(() => prevMapLane.stopLine.intersection.CheckStopSignQueue(controller)));

        hasReachedStopSign = false;
        isStopSign         = false;
    }
Пример #16
0
 void StartStoppingCoroutine()
 {
     if (currentMapLane?.stopLine != null) // check if stopline is connected to current path
     {
         controller.currentIntersection = currentMapLane.stopLine?.intersection;
         stopTarget  = currentMapLane.mapWorldPositions[currentMapLane.mapWorldPositions.Count - 1];
         prevMapLane = currentMapLane;
         if (prevMapLane.stopLine.intersection != null) // null if map not setup right TODO add check to report missing stopline
         {
             if (prevMapLane.stopLine.isStopSign)       // stop sign
             {
                 controller.Coroutines.Add(FixedUpdateManager.StartCoroutine(WaitStopSign()));
             }
             else
             {
                 controller.Coroutines.Add(FixedUpdateManager.StartCoroutine(WaitTrafficLight()));
             }
         }
     }
 }
Пример #17
0
    private void EvaluateLane()
    {
        CurrentIndex++; // index can equal laneData.Count so it can finish npc move IE
        if (CurrentIndex < LaneData.Count)
        {
            CurrentTarget            = LaneData[CurrentIndex];
            controller.MovementSpeed = LaneSpeed[CurrentIndex];
        }

        if (CurrentIndex == LaneData.Count)
        {
            var api = ApiManager.Instance;
            if (WaypointLoop)
            {
                if (CurrentLoopIndex == 0 && api != null)
                {
                    api.AgentTraversedWaypoints(gameObject);
                }
                CurrentLoopIndex++;
                rb.MovePosition(InitPos);
                rb.MoveRotation(InitRot);
                InitNPC();
            }
            else
            {
                if (api != null)
                {
                    api.AgentTraversedWaypoints(gameObject);
                }
                WaypointState = WaypointDriveState.Despawn;
                FixedUpdateManager.StopAllCoroutines();
                TriggerCoroutine = null;
                IdleCoroutine    = null;
                MoveCoroutine    = null;
            }
        }
        else
        {
            WaypointState = WaypointDriveState.Drive;
        }
    }
Пример #18
0
    protected void EvaluateTarget()
    {
        distanceToCurrentTarget = Vector3.Distance(new Vector3(controller.frontCenter.position.x, 0f, controller.frontCenter.position.z), new Vector3(currentTarget.x, 0f, currentTarget.z));
        distanceToStopTarget    = Vector3.Distance(new Vector3(controller.frontCenter.position.x, 0f, controller.frontCenter.position.z), new Vector3(stopTarget.x, 0f, stopTarget.z));

        if (distanceToStopTarget < 1f)
        {
            if (!atStopTarget)
            {
                ApiManager.Instance?.AddStopLine(gameObject);
                atStopTarget = true;
            }
        }
        else
        {
            atStopTarget = false;
        }

        // check if we are past the target or reached current target
        if (Vector3.Dot(controller.frontCenter.forward, (currentTarget - controller.frontCenter.position).normalized) < 0 || distanceToCurrentTarget < 1f)
        {
            if (currentIndex == laneData.Count - 2) // reached 2nd to last target index see if stop line is present
            {
                StartStoppingCoroutine();
            }

            if (currentIndex < laneData.Count - 1) // reached target dist and is not at last index of lane data
            {
                currentIndex++;
                currentTarget = laneData[currentIndex];
                controller.Coroutines.Add(FixedUpdateManager.StartCoroutine(DelayChangeLane()));
            }
            else
            {
                GetNextLane();
            }
        }
    }
Пример #19
0
    private IEnumerator NPCMoveIE()
    {
        if (CurrentIndex == 0)
        {
            // increment index since spawn is index = 0 with no passed params
            EvaluateLane();
        }

        if (CurrentIndex != 0)
        {
            var duration    = LaneTime[CurrentIndex] - LaneTime[CurrentIndex - 1];
            var elapsedTime = 0f;
            while (elapsedTime < duration)
            {
                var factor = elapsedTime / duration;
                var pose   = Vector3.Lerp(LaneData[CurrentIndex - 1], LaneData[CurrentIndex], factor);
                var rot    = Quaternion.Slerp(Quaternion.Euler(LaneAngle[CurrentIndex - 1]), Quaternion.Euler(LaneAngle[CurrentIndex]), factor);
                if (!float.IsNaN(pose.x))
                {
                    rb.MovePosition(pose);
                    rb.MoveRotation(rot);
                }
                elapsedTime += Mathf.Min(Time.fixedDeltaTime, duration - elapsedTime);
                yield return(new WaitForFixedUpdate());
            }
            rb.MovePosition(LaneData[CurrentIndex]);
            rb.MoveRotation(Quaternion.Euler(LaneAngle[CurrentIndex]));
        }

        if (CurrentIndex <= LaneData.Count - 1)
        {
            ApiManager.Instance?.AddWaypointReached(gameObject, CurrentIndex);

            // trigger
            if (LaneTriggerDistance[CurrentIndex] > 0)
            {
                WaypointState = WaypointDriveState.Trigger;
                yield return(TriggerCoroutine = FixedUpdateManager.StartCoroutine(NPCTriggerIE()));
            }

            // deactivate
            CurrentDeactivate = LaneDeactivate[CurrentIndex];

            // idle
            if (LaneIdle[CurrentIndex] > 0)
            {
                WaypointState = WaypointDriveState.Idle;
                yield return(IdleCoroutine = FixedUpdateManager.StartCoroutine(NPCIdleIE(LaneIdle[CurrentIndex], CurrentDeactivate)));
            }
            else if (LaneIdle[CurrentIndex] == -1 && CurrentDeactivate)
            {
                WaypointState = WaypointDriveState.Despawn;
                gameObject.SetActive(false);
                yield break;
            }
            else
            {
                // lane
                EvaluateLane();
            }
        }
        MoveCoroutine = null;
    }
Пример #20
0
    private IEnumerator NPCMoveIE()
    {
        if (CurrentIndex == 0)
        {
            // increment index since spawn is index = 0 with no passed params
            EvaluateLane();
        }

        if (CurrentIndex != 0)
        {
            var duration    = LaneTime[CurrentIndex] - LaneTime[CurrentIndex - 1];
            var elapsedTime = 0f;
            while (elapsedTime < duration)
            {
                var factor = elapsedTime / duration;
                var pose   = Vector3.Lerp(LaneData[CurrentIndex - 1], LaneData[CurrentIndex], factor);
                var rot    = Quaternion.Slerp(Quaternion.Euler(LaneAngle[CurrentIndex - 1]), Quaternion.Euler(LaneAngle[CurrentIndex]), factor);
                if (!float.IsNaN(pose.x))
                {
                    rb.MovePosition(pose);
                    rb.MoveRotation(rot);
                }
                elapsedTime += Mathf.Min(Time.fixedDeltaTime, duration - elapsedTime);
                yield return(new WaitForFixedUpdate());
            }
            rb.MovePosition(LaneData[CurrentIndex]);
            rb.MoveRotation(Quaternion.Euler(LaneAngle[CurrentIndex]));
        }

        if (CurrentIndex <= LaneData.Count - 1)
        {
            //LaneData includes npc position at 0 index, waypoints starts from index 1
            //Because of that index has to be lowered by 1 before passing to the API
            if (ApiManager.Instance != null)
            {
                ApiManager.Instance.AddWaypointReached(gameObject, CurrentIndex - 1);
            }

            // apply simple distance trigger
            if (LaneTriggerDistance[CurrentIndex] > 0)
            {
                WaypointState = WaypointDriveState.Trigger;
                yield return(TriggerCoroutine = FixedUpdateManager.StartCoroutine(NPCTriggerIE()));
            }

            // apply complex triggers
            if (CurrentIndex < LaneTriggers.Count && LaneTriggers[CurrentIndex] != null)
            {
                WaypointState = WaypointDriveState.Trigger;
                yield return(TriggerCoroutine = FixedUpdateManager.StartCoroutine(LaneTriggers[CurrentIndex].Apply(controller)));

                TriggerCoroutine = null;
            }

            // deactivate
            CurrentDeactivate = LaneDeactivate[CurrentIndex];

            // idle
            if (LaneIdle[CurrentIndex] > 0)
            {
                WaypointState = WaypointDriveState.Idle;
                yield return(IdleCoroutine = FixedUpdateManager.StartCoroutine(NPCIdleIE(LaneIdle[CurrentIndex], CurrentDeactivate)));
            }
            else if (LaneIdle[CurrentIndex] == -1 && CurrentDeactivate)
            {
                WaypointState = WaypointDriveState.Despawn;
                gameObject.SetActive(false);
                MoveCoroutine = null;
                yield break;
            }
            else
            {
                // lane
                EvaluateLane();
            }
        }
        MoveCoroutine = null;
    }
Пример #21
0
    protected virtual void EvaluateTarget()
    {
        distanceToCurrentTarget = Vector3.Distance(new Vector3(controller.frontCenter.position.x, 0f, controller.frontCenter.position.z), new Vector3(currentTarget.x, 0f, currentTarget.z));
        distanceToStopTarget    = Vector3.Distance(new Vector3(controller.frontCenter.position.x, 0f, controller.frontCenter.position.z), new Vector3(stopTarget.x, 0f, stopTarget.z));

        if (distanceToStopTarget < 1f)
        {
            if (!atStopTarget)
            {
                ApiManager.Instance?.AddStopLine(gameObject);
                atStopTarget = true;
            }
        }
        else
        {
            atStopTarget = false;
        }

        // check if we are past the target or reached current target
        if (Vector3.Dot(controller.frontCenter.forward, (currentTarget - controller.frontCenter.position).normalized) < 0 || distanceToCurrentTarget < 1f)
        {
            if (currentIndex == laneData.Count - 2) // reached 2nd to last target index see if stop line is present
            {
                StartStoppingCoroutine();
            }

            if (currentIndex < laneData.Count - 1) // reached target dist and is not at last index of lane data
            {
                currentIndex++;
                currentTarget = laneData[currentIndex];
                controller.Coroutines.Add(FixedUpdateManager.StartCoroutine(DelayChangeLane()));
            }
            else
            {
                // GetNextLane
                // last index of current lane data
                if (currentMapLane?.nextConnectedLanes.Count >= 1) // choose next path and set waypoints
                {
                    currentMapLane       = currentMapLane.nextConnectedLanes[RandomGenerator.Next(currentMapLane.nextConnectedLanes.Count)];
                    laneSpeedLimit       = currentMapLane.speedLimit;
                    aggressionAdjustRate = laneSpeedLimit / 11.176f; // 11.176 m/s corresponds to 25 mph
                    normalSpeed          = APIMaxSpeed > 0 ?
                                           Mathf.Min(APIMaxSpeed, laneSpeedLimit) :
                                           RandomGenerator.NextFloat(laneSpeedLimit, laneSpeedLimit + 1 + aggression); // API set max speed or lane speed limit
                    SetLaneData(currentMapLane.mapWorldPositions);
                    SetTurnSignal();
                }
                else
                {
                    Despawn(); // issue getting new waypoints so despawn
                }
            }
        }

        // isTurn
        if (currentMapLane == null)
        {
            return;
        }

        var path = transform.InverseTransformPoint(currentTarget).x;

        isCurve = path <-1f || path> 1f ? true : false;
    }