コード例 #1
0
        public void Update(EnemyAIController enemyAI, float deltaTime)
        {
            if (character.Submarine != null)
            {
                if (targetCharacter != null && targetCharacter.Submarine != targetSubmarine ||
                    character.Submarine != null && targetSubmarine != null && targetCharacter == null)
                {
                    DeattachFromBody(reset: true);
                    return;
                }
            }
            if (IsAttached)
            {
                if (Math.Sign(attachLimb.Dir) != Math.Sign(jointDir))
                {
                    var attachJoint = AttachJoints[0];
                    if (attachJoint is WeldJoint weldJoint)
                    {
                        weldJoint.LocalAnchorA   = new Vector2(-weldJoint.LocalAnchorA.X, weldJoint.LocalAnchorA.Y);
                        weldJoint.ReferenceAngle = -weldJoint.ReferenceAngle;
                    }
                    else if (attachJoint is RevoluteJoint revoluteJoint)
                    {
                        revoluteJoint.LocalAnchorA   = new Vector2(-revoluteJoint.LocalAnchorA.X, revoluteJoint.LocalAnchorA.Y);
                        revoluteJoint.ReferenceAngle = -revoluteJoint.ReferenceAngle;
                    }
                    jointDir = attachLimb.Dir;
                }
                for (int i = 0; i < AttachJoints.Count; i++)
                {
                    //something went wrong, limb body is very far from the joint anchor -> deattach
                    if (Vector2.DistanceSquared(AttachJoints[i].WorldAnchorB, AttachJoints[i].BodyA.Position) > 10.0f * 10.0f)
                    {
#if DEBUG
                        DebugConsole.Log("Limb body of the character \"" + character.Name + "\" is very far from the attach joint anchor -> deattach");
#endif
                        DeattachFromBody(reset: true);
                        return;
                    }
                }
                if (targetCharacter != null)
                {
                    if (enemyAI.AttackingLimb?.attack == null)
                    {
                        DeattachFromBody(reset: true, cooldown: 1);
                    }
                    else
                    {
                        float range = enemyAI.AttackingLimb.attack.DamageRange * 2f;
                        if (Vector2.DistanceSquared(targetCharacter.WorldPosition, enemyAI.AttackingLimb.WorldPosition) > range * range)
                        {
                            DeattachFromBody(reset: true, cooldown: 1);
                        }
                    }
                }
            }

            if (attachCooldown > 0)
            {
                attachCooldown -= deltaTime;
            }
            if (deattachCheckTimer > 0)
            {
                deattachCheckTimer -= deltaTime;
            }

            if (targetCharacter != null)
            {
                // Own sim pos -> target where we are
                _attachPos = character.SimPosition;
            }
            Vector2 transformedAttachPos = _attachPos;
            if (character.Submarine == null && targetSubmarine != null)
            {
                transformedAttachPos += ConvertUnits.ToSimUnits(targetSubmarine.Position);
            }
            if (transformedAttachPos != Vector2.Zero)
            {
                AttachPos = transformedAttachPos;
            }

            switch (enemyAI.State)
            {
            case AIState.Idle:
                if (AttachToWalls && character.Submarine == null && Level.Loaded != null)
                {
                    if (!IsAttached)
                    {
                        raycastTimer -= deltaTime;
                        //check if there are any walls nearby the character could attach to
                        if (raycastTimer < 0.0f)
                        {
                            _attachPos = Vector2.Zero;

                            var cells = Level.Loaded.GetCells(character.WorldPosition, 1);
                            if (cells.Count > 0)
                            {
                                float closestDist = float.PositiveInfinity;
                                foreach (Voronoi2.VoronoiCell cell in cells)
                                {
                                    foreach (Voronoi2.GraphEdge edge in cell.Edges)
                                    {
                                        if (MathUtils.GetLineIntersection(edge.Point1, edge.Point2, character.WorldPosition, cell.Center, out Vector2 intersection))
                                        {
                                            Vector2 potentialAttachPos = ConvertUnits.ToSimUnits(intersection);
                                            float   distSqr            = Vector2.DistanceSquared(character.SimPosition, potentialAttachPos);
                                            if (distSqr < closestDist)
                                            {
                                                attachSurfaceNormal = edge.GetNormal(cell);
                                                targetBody          = cell.Body;
                                                _attachPos          = potentialAttachPos;
                                                closestDist         = distSqr;
                                            }
                                            break;
                                        }
                                    }
                                }
                            }
                            raycastTimer = RaycastInterval;
                        }
                    }
                }
                else
                {
                    _attachPos = Vector2.Zero;
                }
                if (_attachPos == Vector2.Zero || targetBody == null)
                {
                    DeattachFromBody(reset: false);
                }
                else
                {
                    float squaredDistance = Vector2.DistanceSquared(character.SimPosition, _attachPos);
                    float targetDistance  = Math.Max(Math.Max(character.AnimController.Collider.radius, character.AnimController.Collider.width), character.AnimController.Collider.height) * 1.2f;
                    if (squaredDistance < targetDistance * targetDistance)
                    {
                        //close enough to a wall -> attach
                        AttachToBody(_attachPos);
                        enemyAI.SteeringManager.Reset();
                    }
                    else
                    {
                        //move closer to the wall
                        DeattachFromBody(reset: false);
                        enemyAI.SteeringManager.SteeringAvoid(deltaTime, 1.0f, 0.1f);
                        enemyAI.SteeringManager.SteeringSeek(_attachPos);
                    }
                }
                break;

            case AIState.Attack:
            case AIState.Aggressive:
                if (enemyAI.IsSteeringThroughGap)
                {
                    break;
                }
                if (_attachPos == Vector2.Zero)
                {
                    break;
                }
                if (!AttachToSub && !AttachToCharacters)
                {
                    break;
                }
                if (enemyAI.AttackingLimb == null)
                {
                    break;
                }
                if (targetBody == null)
                {
                    break;
                }
                if (IsAttached && AttachJoints[0].BodyB == targetBody)
                {
                    break;
                }
                Vector2 referencePos = targetCharacter != null ? targetCharacter.WorldPosition : ConvertUnits.ToDisplayUnits(transformedAttachPos);
                if (Vector2.DistanceSquared(referencePos, enemyAI.AttackingLimb.WorldPosition) < enemyAI.AttackingLimb.attack.DamageRange * enemyAI.AttackingLimb.attack.DamageRange)
                {
                    AttachToBody(transformedAttachPos);
                }
                break;

            default:
                DeattachFromBody(reset: true);
                break;
            }

            if (IsAttached && targetBody != null && deattachCheckTimer <= 0.0f)
            {
                bool deattach = false;
                if (maxAttachDuration > 0)
                {
                    deattach       = true;
                    attachCooldown = coolDown;
                }
                if (!deattach && targetWall != null && targetSubmarine != null)
                {
                    // Deattach if the wall is broken enough where we are attached to
                    int targetSection = targetWall.FindSectionIndex(attachLimb.WorldPosition, world: true, clamp: true);
                    if (enemyAI.CanPassThroughHole(targetWall, targetSection))
                    {
                        deattach       = true;
                        attachCooldown = coolDown;
                    }
                    if (!deattach)
                    {
                        // Deattach if the velocity is high
                        float velocity = targetSubmarine.Velocity == Vector2.Zero ? 0.0f : targetSubmarine.Velocity.Length();
                        deattach = velocity > maxDeattachSpeed;
                        if (!deattach)
                        {
                            if (velocity > minDeattachSpeed)
                            {
                                float velocityFactor = (maxDeattachSpeed - minDeattachSpeed <= 0.0f) ?
                                                       Math.Sign(Math.Abs(velocity) - minDeattachSpeed) :
                                                       (Math.Abs(velocity) - minDeattachSpeed) / (maxDeattachSpeed - minDeattachSpeed);

                                if (Rand.Range(0.0f, 1.0f) < velocityFactor)
                                {
                                    deattach = true;
                                    character.AddDamage(character.WorldPosition, new List <Affliction>()
                                    {
                                        AfflictionPrefab.InternalDamage.Instantiate(damageOnDetach)
                                    }, detachStun, true);
                                    attachCooldown = Math.Max(detachStun * 2, coolDown);
                                }
                            }
                        }
                    }
                    deattachCheckTimer = 5.0f;
                }
                if (deattach)
                {
                    DeattachFromBody(reset: true);
                }
            }
        }
コード例 #2
0
        /// <summary>
        /// Returns a dictionary where the keys are the structures that took damage and the values are the amount of damage taken
        /// </summary>
        public static Dictionary <Structure, float> RangedStructureDamage(Vector2 worldPosition, float worldRange, float damage, float levelWallDamage, Character attacker = null)
        {
            List <Structure> structureList = new List <Structure>();
            float            dist          = 600.0f;

            foreach (MapEntity entity in MapEntity.mapEntityList)
            {
                if (!(entity is Structure structure))
                {
                    continue;
                }

                if (structure.HasBody &&
                    !structure.IsPlatform &&
                    Vector2.Distance(structure.WorldPosition, worldPosition) < dist * 3.0f)
                {
                    structureList.Add(structure);
                }
            }

            Dictionary <Structure, float> damagedStructures = new Dictionary <Structure, float>();

            foreach (Structure structure in structureList)
            {
                for (int i = 0; i < structure.SectionCount; i++)
                {
                    float distFactor = 1.0f - (Vector2.Distance(structure.SectionPosition(i, true), worldPosition) / worldRange);
                    if (distFactor <= 0.0f)
                    {
                        continue;
                    }

                    structure.AddDamage(i, damage * distFactor, attacker);

                    if (damagedStructures.ContainsKey(structure))
                    {
                        damagedStructures[structure] += damage * distFactor;
                    }
                    else
                    {
                        damagedStructures.Add(structure, damage * distFactor);
                    }
                }
            }

            if (Level.Loaded != null && !MathUtils.NearlyEqual(levelWallDamage, 0.0f))
            {
                for (int i = Level.Loaded.ExtraWalls.Count - 1; i >= 0; i--)
                {
                    if (!(Level.Loaded.ExtraWalls[i] is DestructibleLevelWall destructibleWall))
                    {
                        continue;
                    }
                    foreach (var cell in destructibleWall.Cells)
                    {
                        if (cell.IsPointInside(worldPosition))
                        {
                            destructibleWall.AddDamage(levelWallDamage, worldPosition);
                            continue;
                        }
                        foreach (var edge in cell.Edges)
                        {
                            if (!MathUtils.GetLineIntersection(worldPosition, cell.Center, edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, out Vector2 intersection))
                            {
                                continue;
                            }

                            float wallDist = Vector2.DistanceSquared(worldPosition, intersection);
                            if (wallDist < worldRange * worldRange)
                            {
                                destructibleWall.AddDamage(damage, worldPosition);
                                break;
                            }
                        }
                    }
                }
            }

            return(damagedStructures);
        }