/// <summary>
    /// Handles the case where the current visual threat is a Visual_light.
    /// </summary>
    /// <returns>A new state or NONE if nothing needed to be handled</returns>
    private AiStateType HandleVisualLightThreats()
    {
        AiStateType state = AiStateType.None;

        AiThreatManager manager      = zombieStateMachine.ThreatManager;
        AiTarget        visualThreat = zombieStateMachine.ThreatManager.CurrentVisualThreat;

        // and we currently have a lower priority target then drop into alerted
        // mode and try to find source of light
        if (manager.IsTargeting(AiTargetType.Audio) || manager.IsTargeting(AiTargetType.Visual_Food))
        {
            state = AiStateType.Alerted;
        }
        else if (manager.IsTargeting(AiTargetType.Visual_Light))
        {
            // Get unique ID of the collider of our target
            int currentID = zombieStateMachine.ThreatManager.CurrentTarget.GetColliderID();

            // If this is the same light
            if (currentID == visualThreat.GetColliderID())
            {
                RepathToThreatIfNecessary(visualThreat);
                state = AiStateType.Pursuit;
            }
            else
            {
                state = AiStateType.Alerted;
            }
        }

        manager.TrackTarget(visualThreat);

        return(state);
    }
    /// <summary>
    /// Handles the case where the current Audio AiTarget contains a current Audio Threat.
    /// </summary>
    /// <returns>A new state or NONE if nothing needed to be handled</returns>
    private AiStateType HandleAudioThreat()
    {
        AiStateType state = AiStateType.None;

        AiThreatManager manager     = zombieStateMachine.ThreatManager;
        AiTarget        audioThreat = zombieStateMachine.ThreatManager.CurrentAudioThreat;

        if (manager.IsTargeting(AiTargetType.Visual_Food))
        {
            state = AiStateType.Alerted;
        }
        else if (manager.IsTargeting(AiTargetType.Audio))
        {
            // Get unique ID of the collider of our target
            int currentID = zombieStateMachine.ThreatManager.CurrentTarget.GetColliderID();

            // If this is the same light
            if (currentID == zombieStateMachine.ThreatManager.CurrentAudioThreat.GetColliderID())
            {
                RepathToThreatIfNecessary(audioThreat);
                state = AiStateType.Pursuit;
            }
            else
            {
                state = AiStateType.Alerted;
            }
        }

        manager.TrackTarget(audioThreat);

        return(state);
    }
 /// <summary>
 /// Indicates whether or not to perform the expensive repath to the target.  We want to repath less
 /// frequently as we get closer to the target.  Thus, need to calculate distance to the target and
 /// clamp it to our min/max times.
 /// </summary>
 /// <returns>true if we should repath to the target's location</returns>
 private bool ShouldRepath(AiTarget threat)
 {
     return(Mathf.Clamp(
                threat.Distance * this.repathDistanceMultiplier,
                this.repathVisualMinDuration, this.repathVisualMaxDuration
                ) < this.repathTimer);
 }
 /// <summary>
 /// Indicates whether or not the zombie is hungry enough and can satisfy that hunger because the given target is within range.
 /// </summary>
 /// <param name="target">Should represent a visual food target.</param>
 /// <returns>True if the zombie is hungry enough and can reach the target.</returns>
 public bool CanHungerBeSatisfied(AiTarget target)
 {
     if (target.Type != AiTargetType.Visual_Food)
     {
         Debug.LogWarning("Received invalid target when checking to see if hunger could be satisfied: " + target.ToString());
         return(false);
     }
     return((MAX_SATISFACTION - Satisfaction) > (target.Distance / Sensor.WorldRadius));
 }
 /// <summary>
 /// Performs the repath to the given threat if necessary.
 /// </summary>
 /// <param name="threat">The threat to repath to.</param>
 private void RepathToThreatIfNecessary(AiTarget threat)
 {
     // The position must be different different - maybe same threat but it has moved so repath periodically
     // and our repath timer must be ready to repath so we can save on cpu cycles
     if (zombieStateMachine.ThreatManager.CurrentTarget.Position != threat.Position && ShouldRepath(threat))
     {
         zombieStateMachine.NavAgent.SetDestination(threat.Position);
         ResetRepathTimer();
     }
 }
    /// <summary>
    /// Called by the state machine each frame.
    /// </summary>
    /// <returns>Either the current state or a new state.</returns>
    public override AiStateType OnUpdate()
    {
        UpdateTimers();

        if (HasReachedMaxTime())
        {
            // reset the waypoint but stay in the alerted state with a fresh timer
            zombieStateMachine.WaypointManager.TrackWayPoint();
            ResetMaxDurationTimer();
        }

        AiTarget?   potentialThreat = zombieStateMachine.ThreatManager.DeterminePotentialThreat();
        AiStateType state           = zombieStateMachine.ThreatManager.DetermineNextPotentialThreatState(potentialThreat);

        if (state == AiStateType.None)
        {
            state = GetDefaultStateType();
        }
        else
        {
            AiTarget newThreat = (AiTarget)potentialThreat;
            if (newThreat.Type == AiTargetType.Visual_Player)
            {
                zombieStateMachine.ThreatManager.TrackTarget(newThreat);
            }
            else
            {
                if (newThreat.Type == AiTargetType.Audio || newThreat.Type == AiTargetType.Visual_Light)
                {
                    zombieStateMachine.ThreatManager.TrackTarget(newThreat);
                    ResetMaxDurationTimer();
                }

                if (newThreat.Type == AiTargetType.Visual_Food)
                {
                    if (zombieStateMachine.ThreatManager.DoesTargetExist())
                    {
                        state = GetDefaultStateType(); // food is less of a priority, so reset back to alerted state (audio or light)
                    }
                    else
                    {
                        zombieStateMachine.ThreatManager.TrackTarget(newThreat);
                    }
                }
            }
        }

        if (state == GetDefaultStateType())
        {
            state = HandleDefaultState();
        }

        return(state);
    }
Beispiel #7
0
        private void Draw(SpriteBatch spriteBatch, bool editing, bool back = true, Effect damageEffect = null)
        {
            if (prefab.sprite == null)
            {
                return;
            }
            if (editing)
            {
                if (!HasBody && !ShowStructures)
                {
                    return;
                }
                if (HasBody && !ShowWalls)
                {
                    return;
                }
            }

            Color color = IsHighlighted ? Color.Orange : spriteColor;

            if (IsSelected && editing)
            {
                //color = Color.Lerp(color, Color.Gold, 0.5f);
                color = spriteColor;

                Vector2 rectSize = rect.Size.ToVector2();
                if (BodyWidth > 0.0f)
                {
                    rectSize.X = BodyWidth;
                }
                if (BodyHeight > 0.0f)
                {
                    rectSize.Y = BodyHeight;
                }

                Vector2 bodyPos = WorldPosition + BodyOffset;

                GUI.DrawRectangle(spriteBatch, new Vector2(bodyPos.X, -bodyPos.Y), rectSize.X, rectSize.Y, BodyRotation, Color.White,
                                  thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom)));
            }

            Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;

            float depth = SpriteDepthOverrideIsSet ? SpriteOverrideDepth : prefab.sprite.Depth;

            depth -= (ID % 255) * 0.000001f;

            Vector2 textureOffset = this.textureOffset;

            if (FlippedX)
            {
                textureOffset.X = -textureOffset.X;
            }
            if (FlippedY)
            {
                textureOffset.Y = -textureOffset.Y;
            }

            if (back && damageEffect == null)
            {
                if (Prefab.BackgroundSprite != null)
                {
                    Vector2 dropShadowOffset = Vector2.Zero;
                    if (UseDropShadow)
                    {
                        dropShadowOffset = DropShadowOffset;
                        if (dropShadowOffset == Vector2.Zero)
                        {
                            if (Submarine == null)
                            {
                                dropShadowOffset = Vector2.UnitY * 10.0f;
                            }
                            else
                            {
                                dropShadowOffset = IsHorizontal ?
                                                   new Vector2(0.0f, Math.Sign(Submarine.HiddenSubPosition.Y - Position.Y) * 10.0f) :
                                                   new Vector2(Math.Sign(Submarine.HiddenSubPosition.X - Position.X) * 10.0f, 0.0f);
                            }
                        }
                        dropShadowOffset.Y = -dropShadowOffset.Y;
                    }

                    SpriteEffects oldEffects = Prefab.BackgroundSprite.effects;
                    Prefab.BackgroundSprite.effects ^= SpriteEffects;

                    Point backGroundOffset = new Point(
                        MathUtils.PositiveModulo((int)-textureOffset.X, Prefab.BackgroundSprite.SourceRect.Width),
                        MathUtils.PositiveModulo((int)-textureOffset.Y, Prefab.BackgroundSprite.SourceRect.Height));

                    Prefab.BackgroundSprite.DrawTiled(
                        spriteBatch,
                        new Vector2(rect.X + drawOffset.X, -(rect.Y + drawOffset.Y)),
                        new Vector2(rect.Width, rect.Height),
                        color: color,
                        textureScale: TextureScale * Scale,
                        startOffset: backGroundOffset,
                        depth: Math.Max(Prefab.BackgroundSprite.Depth, depth + 0.000001f));

                    if (UseDropShadow)
                    {
                        Prefab.BackgroundSprite.DrawTiled(
                            spriteBatch,
                            new Vector2(rect.X + drawOffset.X, -(rect.Y + drawOffset.Y)) + dropShadowOffset,
                            new Vector2(rect.Width, rect.Height),
                            color: Color.Black * 0.5f,
                            textureScale: TextureScale * Scale,
                            startOffset: backGroundOffset,
                            depth: (depth + Prefab.BackgroundSprite.Depth) / 2.0f);
                    }

                    Prefab.BackgroundSprite.effects = oldEffects;
                }
            }

            if (back == depth > 0.5f)
            {
                SpriteEffects oldEffects = prefab.sprite.effects;
                prefab.sprite.effects ^= SpriteEffects;

                for (int i = 0; i < Sections.Length; i++)
                {
                    if (damageEffect != null)
                    {
                        float newCutoff = MathHelper.Lerp(0.0f, 0.65f, Sections[i].damage / Prefab.Health);

                        if (Math.Abs(newCutoff - Submarine.DamageEffectCutoff) > 0.01f || color != Submarine.DamageEffectColor)
                        {
                            damageEffect.Parameters["aCutoff"].SetValue(newCutoff);
                            damageEffect.Parameters["cCutoff"].SetValue(newCutoff * 1.2f);
                            damageEffect.Parameters["inColor"].SetValue(color.ToVector4());

                            damageEffect.CurrentTechnique.Passes[0].Apply();

                            Submarine.DamageEffectCutoff = newCutoff;
                            Submarine.DamageEffectColor  = color;
                        }
                    }

                    Point sectionOffset = new Point(
                        Math.Abs(rect.Location.X - Sections[i].rect.Location.X),
                        Math.Abs(rect.Location.Y - Sections[i].rect.Location.Y));

                    if (FlippedX && IsHorizontal)
                    {
                        sectionOffset.X = Sections[i].rect.Right - rect.Right;
                    }
                    if (FlippedY && !IsHorizontal)
                    {
                        sectionOffset.Y = (rect.Y - rect.Height) - (Sections[i].rect.Y - Sections[i].rect.Height);
                    }

                    sectionOffset.X += MathUtils.PositiveModulo((int)-textureOffset.X, prefab.sprite.SourceRect.Width);
                    sectionOffset.Y += MathUtils.PositiveModulo((int)-textureOffset.Y, prefab.sprite.SourceRect.Height);

                    prefab.sprite.DrawTiled(
                        spriteBatch,
                        new Vector2(Sections[i].rect.X + drawOffset.X, -(Sections[i].rect.Y + drawOffset.Y)),
                        new Vector2(Sections[i].rect.Width, Sections[i].rect.Height),
                        color: color,
                        startOffset: sectionOffset,
                        depth: depth,
                        textureScale: TextureScale * Scale);
                }
                prefab.sprite.effects = oldEffects;
            }

            if (GameMain.DebugDraw)
            {
                if (Bodies != null)
                {
                    for (int i = 0; i < Bodies.Count; i++)
                    {
                        Vector2 pos = FarseerPhysics.ConvertUnits.ToDisplayUnits(Bodies[i].Position);
                        if (Submarine != null)
                        {
                            pos += Submarine.Position;
                        }
                        pos.Y = -pos.Y;
                        GUI.DrawRectangle(spriteBatch,
                                          pos,
                                          FarseerPhysics.ConvertUnits.ToDisplayUnits(bodyDebugDimensions[i].X),
                                          FarseerPhysics.ConvertUnits.ToDisplayUnits(bodyDebugDimensions[i].Y),
                                          -Bodies[i].Rotation, Color.White);
                    }
                }

                if (SectionCount > 0 && HasBody)
                {
                    for (int i = 0; i < SectionCount; i++)
                    {
                        if (GetSection(i).damage > 0)
                        {
                            var textPos = SectionPosition(i, true);
                            textPos.Y = -textPos.Y;
                            GUI.DrawString(spriteBatch, textPos, "Damage: " + (int)((GetSection(i).damage / Health) * 100f) + "%", Color.Yellow);
                        }
                    }
                }

                AiTarget?.Draw(spriteBatch);
            }
        }
Beispiel #8
0
    /// <summary>
    /// Indicates whether or not the distance to the given threat is smaller then anything we may have
    /// previously stored
    /// </summary>
    /// <param name="threat">The threat to test</param>
    /// <param name="distanceToThreat">The pre-computed distance to the given threat</param>
    /// <returns>true if a previous threat existed and zombie is now closer to it.</returns>
    private bool IsCloserThanLastThreat(AiTarget threat, float distanceToThreat)
    {
        AiThreatManager manager = zombieStateMachine.ThreatManager;

        return(threat.Type == AiTargetType.None || distanceToThreat < threat.Distance);
    }
Beispiel #9
0
 /// <summary>
 /// Tracks the target using the values of the given target.
 /// </summary>
 public void TrackTarget(AiTarget target)
 {
     this.TrackTarget(target.Type, target.Collider, target.Position, target.Distance, this.stateMachine.StoppingDistance);
 }