Exemplo n.º 1
0
        public static VertexPositionTexture[] GenerateWallShapes(List <VoronoiCell> cells, Level level)
        {
            float outWardThickness = 30.0f;

            List <VertexPositionTexture> verticeList = new List <VertexPositionTexture>();

            foreach (VoronoiCell cell in cells)
            {
                CompareCCW compare = new CompareCCW(cell.Center);
                foreach (GraphEdge edge in cell.Edges)
                {
                    if (edge.Cell1 != null && edge.Cell1.Body == null && edge.Cell1.CellType != CellType.Empty)
                    {
                        edge.Cell1 = null;
                    }
                    if (edge.Cell2 != null && edge.Cell2.Body == null && edge.Cell2.CellType != CellType.Empty)
                    {
                        edge.Cell2 = null;
                    }

                    if (compare.Compare(edge.Point1, edge.Point2) == -1)
                    {
                        var temp = edge.Point1;
                        edge.Point1 = edge.Point2;
                        edge.Point2 = temp;
                    }
                }
            }

            foreach (VoronoiCell cell in cells)
            {
                foreach (GraphEdge edge in cell.Edges)
                {
                    if (!edge.IsSolid)
                    {
                        continue;
                    }

                    GraphEdge leftEdge  = cell.Edges.Find(e => e != edge && (edge.Point1 == e.Point1 || edge.Point1 == e.Point2));
                    GraphEdge rightEdge = cell.Edges.Find(e => e != edge && (edge.Point2 == e.Point1 || edge.Point2 == e.Point2));

                    Vector2 leftNormal = Vector2.Zero, rightNormal = Vector2.Zero;

                    float inwardThickness1 = 100;
                    float inwardThickness2 = 100;
                    if (leftEdge != null && !leftEdge.IsSolid)
                    {
                        leftNormal = edge.Point1 == leftEdge.Point1 ?
                                     Vector2.Normalize(leftEdge.Point2 - leftEdge.Point1) :
                                     Vector2.Normalize(leftEdge.Point1 - leftEdge.Point2);
                        inwardThickness1 = Vector2.Distance(leftEdge.Point1, leftEdge.Point2) / 2;
                    }
                    else
                    {
                        leftNormal       = Vector2.Normalize(cell.Center - edge.Point1);
                        inwardThickness1 = Vector2.Distance(edge.Point1, cell.Center) / 2;
                    }

                    if (!MathUtils.IsValid(leftNormal))
                    {
#if DEBUG
                        DebugConsole.ThrowError("Invalid left normal");
#endif
                        GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidLeftNormal:" + level.Seed,
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
                                                               "Invalid left normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + leftNormal + ", seed: " + level.Seed + ")");

                        if (cell.Body != null)
                        {
                            GameMain.World.Remove(cell.Body);
                            cell.Body = null;
                        }
                        leftNormal = Vector2.UnitX;
                        break;
                    }

                    if (rightEdge != null && !rightEdge.IsSolid)
                    {
                        rightNormal = edge.Point2 == rightEdge.Point1 ?
                                      Vector2.Normalize(rightEdge.Point2 - rightEdge.Point1) :
                                      Vector2.Normalize(rightEdge.Point1 - rightEdge.Point2);
                        inwardThickness2 = Vector2.Distance(rightEdge.Point1, rightEdge.Point2) / 2;
                    }
                    else
                    {
                        rightNormal      = Vector2.Normalize(cell.Center - edge.Point2);
                        inwardThickness2 = Vector2.Distance(edge.Point2, cell.Center) / 2;
                    }

                    if (!MathUtils.IsValid(rightNormal))
                    {
#if DEBUG
                        DebugConsole.ThrowError("Invalid right normal");
#endif
                        GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidRightNormal:" + level.Seed,
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
                                                               "Invalid right normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + rightNormal + ", seed: " + level.Seed + ")");

                        if (cell.Body != null)
                        {
                            GameMain.World.Remove(cell.Body);
                            cell.Body = null;
                        }
                        rightNormal = Vector2.UnitX;
                        break;
                    }

                    float point1UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point1 - cell.Center));
                    float point2UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point2 - cell.Center));
                    //handle wrapping around 0/360
                    if (point1UV - point2UV > MathHelper.Pi)
                    {
                        point2UV += MathHelper.TwoPi;
                    }
                    //the texture wraps around the cell 4 times
                    //TODO: define the uv scale in level generation parameters?
                    point1UV = point1UV / MathHelper.TwoPi * 4;
                    point2UV = point2UV / MathHelper.TwoPi * 4;

                    for (int i = 0; i < 2; i++)
                    {
                        Vector2[] verts = new Vector2[3];
                        VertexPositionTexture[] vertPos = new VertexPositionTexture[3];

                        if (i == 0)
                        {
                            verts[0] = edge.Point1 - leftNormal * outWardThickness;
                            verts[1] = edge.Point2 - rightNormal * outWardThickness;
                            verts[2] = edge.Point1 + leftNormal * inwardThickness1;

                            vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), new Vector2(point1UV, 0.0f));
                            vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), new Vector2(point2UV, 0.0f));
                            vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(point1UV, 0.5f));
                        }
                        else
                        {
                            verts[0] = edge.Point1 + leftNormal * inwardThickness1;
                            verts[1] = edge.Point2 - rightNormal * outWardThickness;
                            verts[2] = edge.Point2 + rightNormal * inwardThickness2;

                            vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), new Vector2(point1UV, 0.5f));
                            vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), new Vector2(point2UV, 0.0f));
                            vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(point2UV, 0.5f));
                        }
                        verticeList.AddRange(vertPos);
                    }
                }
            }

            return(verticeList.ToArray());
        }
Exemplo n.º 2
0
        private void SetDamage(int sectionIndex, float damage, Character attacker = null, bool createNetworkEvent = true)
        {
            if (Submarine != null && Submarine.GodMode || Indestructible)
            {
                return;
            }
            if (!Prefab.Body)
            {
                return;
            }
            if (!MathUtils.IsValid(damage))
            {
                return;
            }

            damage = MathHelper.Clamp(damage, 0.0f, Prefab.Health);

#if SERVER
            if (GameMain.Server != null && createNetworkEvent && damage != Sections[sectionIndex].damage)
            {
                GameMain.Server.CreateEntityEvent(this);
            }
            bool noGaps = true;
            for (int i = 0; i < Sections.Length; i++)
            {
                if (i != sectionIndex && SectionIsLeaking(i))
                {
                    noGaps = false;
                    break;
                }
            }
#endif


            if (damage < Prefab.Health * LeakThreshold)
            {
                if (Sections[sectionIndex].gap != null)
                {
#if SERVER
                    //the structure doesn't have any other gap, log the structure being fixed
                    if (noGaps && attacker != null)
                    {
                        GameServer.Log((Sections[sectionIndex].gap.IsRoomToRoom ? "Inner" : "Outer") + " wall repaired by " + attacker.Name, ServerLog.MessageType.ItemInteraction);
                    }
#endif
                    DebugConsole.Log("Removing gap (ID " + Sections[sectionIndex].gap.ID + ", section: " + sectionIndex + ") from wall " + ID);

                    //remove existing gap if damage is below leak threshold
                    Sections[sectionIndex].gap.Open = 0.0f;
                    Sections[sectionIndex].gap.Remove();
                    Sections[sectionIndex].gap = null;
                }
            }
            else
            {
                if (Sections[sectionIndex].gap == null)
                {
                    Rectangle gapRect = Sections[sectionIndex].rect;
                    float     diffFromCenter;
                    if (IsHorizontal)
                    {
                        diffFromCenter = (gapRect.Center.X - this.rect.Center.X) / (float)this.rect.Width * BodyWidth;
                        if (BodyWidth > 0.0f)
                        {
                            gapRect.Width = (int)(BodyWidth * (gapRect.Width / (float)this.rect.Width));
                        }
                        if (BodyHeight > 0.0f)
                        {
                            gapRect.Height = (int)BodyHeight;
                        }
                    }
                    else
                    {
                        diffFromCenter = ((gapRect.Y - gapRect.Height / 2) - (this.rect.Y - this.rect.Height / 2)) / (float)this.rect.Height * BodyHeight;
                        if (BodyWidth > 0.0f)
                        {
                            gapRect.Width = (int)BodyWidth;
                        }
                        if (BodyHeight > 0.0f)
                        {
                            gapRect.Height = (int)(BodyHeight * (gapRect.Height / (float)this.rect.Height));
                        }
                    }
                    if (FlippedX)
                    {
                        diffFromCenter = -diffFromCenter;
                    }

                    if (BodyRotation != 0.0f)
                    {
                        Vector2 structureCenter = Position;
                        Vector2 gapPos          = structureCenter + new Vector2(
                            (float)Math.Cos(IsHorizontal ? -BodyRotation : MathHelper.PiOver2 - BodyRotation),
                            (float)Math.Sin(IsHorizontal ? -BodyRotation : MathHelper.PiOver2 - BodyRotation)) * diffFromCenter;
                        gapRect = new Rectangle((int)(gapPos.X - gapRect.Width / 2), (int)(gapPos.Y + gapRect.Height / 2), gapRect.Width, gapRect.Height);
                    }

                    gapRect.X      -= 10;
                    gapRect.Y      += 10;
                    gapRect.Width  += 20;
                    gapRect.Height += 20;

                    bool horizontalGap = !IsHorizontal;
                    if (Prefab.BodyRotation != 0.0f)
                    {
                        //rotation within a 90 deg sector (e.g. 100 -> 10, 190 -> 10, -10 -> 80)
                        float sectorizedRotation = MathUtils.WrapAngleTwoPi(BodyRotation) % MathHelper.PiOver2;
                        //diagonal if 30 < angle < 60
                        bool diagonal = sectorizedRotation > MathHelper.Pi / 6 && sectorizedRotation < MathHelper.Pi / 3;
                        //gaps on the lower half of a diagonal wall are horizontal, ones on the upper half are vertical
                        if (diagonal)
                        {
                            horizontalGap = gapRect.Y - gapRect.Height / 2 < Position.Y;
                        }
                    }

                    Sections[sectionIndex].gap = new Gap(gapRect, horizontalGap, Submarine);

                    //free the ID, because if we give gaps IDs we have to make sure they always match between the clients and the server and
                    //that clients create them in the correct order along with every other entity created/removed during the round
                    //which COULD be done via entityspawner, but it's unnecessary because we never access these gaps by ID
                    Sections[sectionIndex].gap.FreeID();
                    Sections[sectionIndex].gap.ShouldBeSaved = false;
                    Sections[sectionIndex].gap.ConnectedWall = this;
                    DebugConsole.Log("Created gap (ID " + Sections[sectionIndex].gap.ID + ", section: " + sectionIndex + ") on wall " + ID);
                    //AdjustKarma(attacker, 300);

#if SERVER
                    //the structure didn't have any other gaps yet, log the breach
                    if (noGaps && attacker != null)
                    {
                        GameServer.Log((Sections[sectionIndex].gap.IsRoomToRoom ? "Inner" : "Outer") + " wall breached by " + attacker.Name, ServerLog.MessageType.ItemInteraction);
                    }
#endif
                }

                float gapOpen = (damage / Prefab.Health - LeakThreshold) * (1.0f / (1.0f - LeakThreshold));
                Sections[sectionIndex].gap.Open = gapOpen;
            }

            float damageDiff = damage - Sections[sectionIndex].damage;
            bool  hadHole    = SectionBodyDisabled(sectionIndex);
            Sections[sectionIndex].damage = MathHelper.Clamp(damage, 0.0f, Prefab.Health);

            //otherwise it's possible to infinitely gain karma by welding fixed things
            if (attacker != null && damageDiff != 0.0f)
            {
                AdjustKarma(attacker, damageDiff);
#if CLIENT
                if (GameMain.Client == null)
                {
#endif
                if (damageDiff < 0.0f)
                {
                    attacker.Info.IncreaseSkillLevel("mechanical",
                                                     -damageDiff * SkillIncreaseMultiplier / Math.Max(attacker.GetSkillLevel("mechanical"), 1.0f),
                                                     SectionPosition(sectionIndex, true));
                }
#if CLIENT
            }
#endif
            }

            bool hasHole = SectionBodyDisabled(sectionIndex);

            if (hadHole == hasHole)
            {
                return;
            }

            UpdateSections();
        }
Exemplo n.º 3
0
        partial void UpdateNetPlayerPositionProjSpecific(float deltaTime, float lowestSubPos)
        {
            if (character != GameMain.Client.Character || !character.CanMove)
            {
                //remove states without a timestamp (there may still be ID-based states
                //in the list when the controlled character switches to timestamp-based interpolation)
                character.MemState.RemoveAll(m => m.Timestamp == 0.0f);

                //use simple interpolation for other players' characters and characters that can't move
                if (character.MemState.Count > 0)
                {
                    CharacterStateInfo serverPos = character.MemState.Last();
                    if (!character.isSynced)
                    {
                        SetPosition(serverPos.Position, false);
                        Collider.LinearVelocity = Vector2.Zero;
                        character.MemLocalState.Clear();
                        character.LastNetworkUpdateID = serverPos.ID;
                        character.isSynced            = true;
                        return;
                    }

                    if (character.MemState[0].SelectedCharacter == null || character.MemState[0].SelectedCharacter.Removed)
                    {
                        character.DeselectCharacter();
                    }
                    else if (character.MemState[0].SelectedCharacter != null)
                    {
                        character.SelectCharacter(character.MemState[0].SelectedCharacter);
                    }

                    if (character.MemState[0].SelectedItem == null || character.MemState[0].SelectedItem.Removed)
                    {
                        character.SelectedConstruction = null;
                    }
                    else
                    {
                        if (character.SelectedConstruction != character.MemState[0].SelectedItem)
                        {
                            foreach (var ic in character.MemState[0].SelectedItem.Components)
                            {
                                if (ic.CanBeSelected)
                                {
                                    ic.Select(character);
                                }
                            }
                        }
                        character.SelectedConstruction = character.MemState[0].SelectedItem;
                    }

                    if (character.MemState[0].Animation == AnimController.Animation.CPR)
                    {
                        character.AnimController.Anim = AnimController.Animation.CPR;
                    }
                    else if (character.AnimController.Anim == AnimController.Animation.CPR)
                    {
                        character.AnimController.Anim = AnimController.Animation.None;
                    }

                    Vector2 newVelocity        = Collider.LinearVelocity;
                    Vector2 newPosition        = Collider.SimPosition;
                    float   newRotation        = Collider.Rotation;
                    float   newAngularVelocity = Collider.AngularVelocity;
                    Collider.CorrectPosition(character.MemState, out newPosition, out newVelocity, out newRotation, out newAngularVelocity);

                    newVelocity = newVelocity.ClampLength(100.0f);
                    if (!MathUtils.IsValid(newVelocity))
                    {
                        newVelocity = Vector2.Zero;
                    }
                    overrideTargetMovement = newVelocity.LengthSquared() > 0.01f ? newVelocity : Vector2.Zero;

                    Collider.LinearVelocity  = newVelocity;
                    Collider.AngularVelocity = newAngularVelocity;

                    float distSqrd       = Vector2.DistanceSquared(newPosition, Collider.SimPosition);
                    float errorTolerance = character.CanMove ? 0.01f : 0.2f;
                    if (distSqrd > errorTolerance)
                    {
                        if (distSqrd > 10.0f || !character.CanMove)
                        {
                            Collider.TargetRotation = newRotation;
                            SetPosition(newPosition, lerp: distSqrd < 5.0f, ignorePlatforms: false);
                        }
                        else
                        {
                            Collider.TargetRotation = newRotation;
                            Collider.TargetPosition = newPosition;
                            Collider.MoveToTargetPosition(true);
                        }
                    }

                    //immobilized characters can't correct their position using AnimController movement
                    // -> we need to correct it manually
                    if (!character.CanMove)
                    {
                        float mainLimbDistSqrd       = Vector2.DistanceSquared(MainLimb.PullJointWorldAnchorA, Collider.SimPosition);
                        float mainLimbErrorTolerance = 0.1f;
                        //if the main limb is roughly at the correct position and the collider isn't moving (much at least),
                        //don't attempt to correct the position.
                        if (mainLimbDistSqrd > mainLimbErrorTolerance || Collider.LinearVelocity.LengthSquared() > 0.05f)
                        {
                            MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
                            MainLimb.PullJointEnabled      = true;
                        }
                    }
                }
                character.MemLocalState.Clear();
            }
            else
            {
                //remove states with a timestamp (there may still timestamp-based states
                //in the list if the controlled character switches from timestamp-based interpolation to ID-based)
                character.MemState.RemoveAll(m => m.Timestamp > 0.0f);

                for (int i = 0; i < character.MemLocalState.Count; i++)
                {
                    if (character.Submarine == null)
                    {
                        //transform in-sub coordinates to outside coordinates
                        if (character.MemLocalState[i].Position.Y > lowestSubPos)
                        {
                            character.MemLocalState[i].TransformInToOutside();
                        }
                    }
                    else if (currentHull?.Submarine != null)
                    {
                        //transform outside coordinates to in-sub coordinates
                        if (character.MemLocalState[i].Position.Y < lowestSubPos)
                        {
                            character.MemLocalState[i].TransformOutToInside(currentHull.Submarine);
                        }
                    }
                }

                if (character.MemState.Count < 1)
                {
                    return;
                }

                overrideTargetMovement = Vector2.Zero;

                CharacterStateInfo serverPos = character.MemState.Last();

                if (!character.isSynced)
                {
                    SetPosition(serverPos.Position, false);
                    Collider.LinearVelocity = Vector2.Zero;
                    character.MemLocalState.Clear();
                    character.LastNetworkUpdateID = serverPos.ID;
                    character.isSynced            = true;
                    return;
                }

                int localPosIndex = character.MemLocalState.FindIndex(m => m.ID == serverPos.ID);
                if (localPosIndex > -1)
                {
                    CharacterStateInfo localPos = character.MemLocalState[localPosIndex];

                    //the entity we're interacting with doesn't match the server's
                    if (localPos.SelectedCharacter != serverPos.SelectedCharacter)
                    {
                        if (serverPos.SelectedCharacter == null || serverPos.SelectedCharacter.Removed)
                        {
                            character.DeselectCharacter();
                        }
                        else if (serverPos.SelectedCharacter != null)
                        {
                            character.SelectCharacter(serverPos.SelectedCharacter);
                        }
                    }
                    if (localPos.SelectedItem != serverPos.SelectedItem)
                    {
                        if (serverPos.SelectedItem == null || serverPos.SelectedItem.Removed)
                        {
                            character.SelectedConstruction = null;
                        }
                        else if (serverPos.SelectedItem != null)
                        {
                            if (character.SelectedConstruction != serverPos.SelectedItem)
                            {
                                serverPos.SelectedItem.TryInteract(character, true, true);
                            }
                            character.SelectedConstruction = serverPos.SelectedItem;
                        }
                    }

                    if (localPos.Animation != serverPos.Animation)
                    {
                        if (serverPos.Animation == AnimController.Animation.CPR)
                        {
                            character.AnimController.Anim = AnimController.Animation.CPR;
                        }
                        else if (character.AnimController.Anim == AnimController.Animation.CPR)
                        {
                            character.AnimController.Anim = AnimController.Animation.None;
                        }
                    }

                    Hull serverHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(serverPos.Position), character.CurrentHull, serverPos.Position.Y < lowestSubPos);
                    Hull clientHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(localPos.Position), serverHull, localPos.Position.Y < lowestSubPos);

                    if (serverHull != null && clientHull != null && serverHull.Submarine != clientHull.Submarine)
                    {
                        //hull subs don't match => teleport the camera to the other sub
                        character.Submarine   = serverHull.Submarine;
                        character.CurrentHull = CurrentHull = serverHull;
                        SetPosition(serverPos.Position);
                        character.MemLocalState.Clear();
                    }
                    else
                    {
                        Vector2 positionError = serverPos.Position - localPos.Position;
                        float   rotationError = serverPos.Rotation.HasValue && localPos.Rotation.HasValue ?
                                                serverPos.Rotation.Value - localPos.Rotation.Value :
                                                0.0f;

                        for (int i = localPosIndex; i < character.MemLocalState.Count; i++)
                        {
                            Hull pointHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(character.MemLocalState[i].Position), clientHull, character.MemLocalState[i].Position.Y < lowestSubPos);
                            if (pointHull != clientHull && ((pointHull == null) || (clientHull == null) || (pointHull.Submarine == clientHull.Submarine)))
                            {
                                break;
                            }
                            character.MemLocalState[i].Translate(positionError, rotationError);
                        }

                        float errorMagnitude = positionError.Length();
                        if (errorMagnitude > 0.5f)
                        {
                            character.MemLocalState.Clear();
                            SetPosition(serverPos.Position, lerp: true, ignorePlatforms: false);
                        }
                        else if (errorMagnitude > 0.01f)
                        {
                            Collider.TargetPosition = Collider.SimPosition + positionError;
                            Collider.TargetRotation = Collider.Rotation + rotationError;
                            Collider.MoveToTargetPosition(lerp: true);
                        }
                    }
                }

                if (character.MemLocalState.Count > 120)
                {
                    character.MemLocalState.RemoveRange(0, character.MemLocalState.Count - 120);
                }
                character.MemState.Clear();
            }
        }
Exemplo n.º 4
0
        private void ApplyImpact(float impact, Vector2 direction, Vector2 impactPos, bool applyDamage = true)
        {
            if (impact < MinCollisionImpact)
            {
                return;
            }

            Vector2 impulse = direction * impact * 0.5f;

            impulse = impulse.ClampLength(MaxCollisionImpact);

            if (!MathUtils.IsValid(impulse))
            {
                string errorMsg =
                    "Invalid impulse in SubmarineBody.ApplyImpact: " + impulse +
                    ". Direction: " + direction + ", body position: " + Body.SimPosition + ", impact: " + impact + ".";
                if (GameMain.NetworkMember != null)
                {
                    errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server.";
                }
                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.ThrowError(errorMsg);
                }
                GameAnalyticsManager.AddErrorEventOnce(
                    "SubmarineBody.ApplyImpact:InvalidImpulse",
                    GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                    errorMsg);
                return;
            }

#if CLIENT
            if (Character.Controlled != null && Character.Controlled.Submarine == submarine && Character.Controlled.KnockbackCooldownTimer <= 0.0f)
            {
                GameMain.GameScreen.Cam.Shake = Math.Max(impact * 10.0f, GameMain.GameScreen.Cam.Shake);
                if (submarine.Info.Type == SubmarineType.Player && !submarine.DockedTo.Any(s => s.Info.Type != SubmarineType.Player))
                {
                    float angularVelocity =
                        (impactPos.X - Body.SimPosition.X) / ConvertUnits.ToSimUnits(submarine.Borders.Width / 2) * impulse.Y
                        - (impactPos.Y - Body.SimPosition.Y) / ConvertUnits.ToSimUnits(submarine.Borders.Height / 2) * impulse.X;
                    GameMain.GameScreen.Cam.AngularVelocity = MathHelper.Clamp(angularVelocity * 0.1f, -1.0f, 1.0f);
                }
            }
#endif

            foreach (Character c in Character.CharacterList)
            {
                if (c.Submarine != submarine)
                {
                    continue;
                }
                if (c.KnockbackCooldownTimer > 0.0f)
                {
                    continue;
                }

                c.KnockbackCooldownTimer = Character.KnockbackCooldown;

                foreach (Limb limb in c.AnimController.Limbs)
                {
                    if (limb.IsSevered)
                    {
                        continue;
                    }
                    limb.body.ApplyLinearImpulse(limb.Mass * impulse, 10.0f);
                }

                bool holdingOntoSomething = false;
                if (c.SelectedConstruction != null)
                {
                    holdingOntoSomething =
                        c.SelectedConstruction.GetComponent <Ladder>() != null ||
                        (c.SelectedConstruction.GetComponent <Controller>()?.LimbPositions.Any() ?? false);
                }

                if (!holdingOntoSomething)
                {
                    c.AnimController.Collider.ApplyLinearImpulse(c.AnimController.Collider.Mass * impulse, 10.0f);
                    //stun for up to 2 second if the impact equal or higher to the maximum impact
                    if (impact >= MaxCollisionImpact)
                    {
                        c.AddDamage(impactPos, AfflictionPrefab.ImpactDamage.Instantiate(3.0f).ToEnumerable(), stun: Math.Min(impulse.Length() * 0.2f, 2.0f), playSound: true);
                    }
                }
            }

            foreach (Item item in Item.ItemList)
            {
                if (item.Submarine != submarine || item.CurrentHull == null || item.body == null || !item.body.Enabled)
                {
                    continue;
                }

                item.body.ApplyLinearImpulse(item.body.Mass * impulse, 10.0f);
                item.PositionUpdateInterval = 0.0f;
            }

            float dmg = applyDamage ? impact * ImpactDamageMultiplier : 0.0f;
            var   damagedStructures = Explosion.RangedStructureDamage(
                ConvertUnits.ToDisplayUnits(impactPos),
                impact * 50.0f,
                dmg, dmg);

#if CLIENT
            PlayDamageSounds(damagedStructures, impactPos, impact, "StructureBlunt");
#endif
        }
Exemplo n.º 5
0
        public override void Update(float deltaTime, Camera cam)
        {
            flowForce = Vector2.Zero;

            outsideColliderRaycastTimer -= deltaTime;

            if (open == 0.0f || linkedTo.Count == 0)
            {
                lerpedFlowForce = Vector2.Zero;
                return;
            }

            UpdateOxygen();

            if (linkedTo.Count == 1)
            {
                //gap leading from a room to outside
                UpdateRoomToOut(deltaTime);
            }
            else
            {
                //gap leading from a room to another
                UpdateRoomToRoom(deltaTime);
            }

            flowForce.X     = MathHelper.Clamp(flowForce.X, -MaxFlowForce, MaxFlowForce);
            flowForce.Y     = MathHelper.Clamp(flowForce.Y, -MaxFlowForce, MaxFlowForce);
            lerpedFlowForce = Vector2.Lerp(lerpedFlowForce, flowForce, deltaTime * 5.0f);

            EmitParticles(deltaTime);

            if (flowTargetHull != null && lerpedFlowForce.LengthSquared() > 0.0001f)
            {
                foreach (Character character in Character.CharacterList)
                {
                    if (character.CurrentHull == null)
                    {
                        continue;
                    }
                    if (character.CurrentHull != linkedTo[0] as Hull &&
                        (linkedTo.Count < 2 || character.CurrentHull != linkedTo[1] as Hull))
                    {
                        continue;
                    }

                    foreach (Limb limb in character.AnimController.Limbs)
                    {
                        if (!limb.inWater)
                        {
                            continue;
                        }

                        float dist = Vector2.Distance(limb.WorldPosition, WorldPosition);
                        if (dist > lerpedFlowForce.Length())
                        {
                            continue;
                        }

                        Vector2 force = lerpedFlowForce / (float)Math.Max(Math.Sqrt(dist), 20.0f) * 0.025f;

                        //vertical gaps only apply forces if the character is roughly above/below the gap
                        if (!IsHorizontal)
                        {
                            float xDist = Math.Abs(limb.WorldPosition.X - WorldPosition.X);
                            if (xDist > rect.Width || rect.Width == 0)
                            {
                                break;
                            }

                            force *= 1.0f - xDist / rect.Width;
                        }

                        if (!MathUtils.IsValid(force))
                        {
                            string errorMsg = "Attempted to apply invalid flow force to the character \"" + character.Name +
                                              "\", gap pos: " + WorldPosition +
                                              ", limb pos: " + limb.WorldPosition +
                                              ", flowforce: " + flowForce + ", lerpedFlowForce:" + lerpedFlowForce +
                                              ", dist: " + dist;

                            DebugConsole.Log(errorMsg);
                            GameAnalyticsManager.AddErrorEventOnce("Gap.Update:InvalidFlowForce:" + character.Name,
                                                                   GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                                   errorMsg);
                            continue;
                        }
                        character.AnimController.Collider.ApplyForce(force * limb.body.Mass, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
                    }
                }
            }
        }
Exemplo n.º 6
0
        void UpdateDying(float deltaTime)
        {
            if (deathAnimDuration <= 0.0f)
            {
                return;
            }

            float noise        = (PerlinNoise.GetPerlin(WalkPos * 0.002f, WalkPos * 0.003f) - 0.5f) * 5.0f;
            float animStrength = (1.0f - deathAnimTimer / deathAnimDuration);

            Limb head = GetLimb(LimbType.Head);

            if (head != null && head.IsSevered)
            {
                return;
            }
            Limb tail = GetLimb(LimbType.Tail);

            if (head != null && !head.IsSevered)
            {
                head.body.ApplyTorque((float)(Math.Sqrt(head.Mass) * Dir * (Math.Sin(WalkPos) + noise)) * 30.0f * animStrength);
            }
            if (tail != null && !tail.IsSevered)
            {
                tail.body.ApplyTorque((float)(Math.Sqrt(tail.Mass) * -Dir * (Math.Sin(WalkPos) + noise)) * 30.0f * animStrength);
            }

            WalkPos += deltaTime * 10.0f * animStrength;

            Vector2 centerOfMass = GetCenterOfMass();

            foreach (Limb limb in Limbs)
            {
                if (limb.IsSevered)
                {
                    continue;
                }
#if CLIENT
                if (limb.LightSource != null)
                {
                    limb.LightSource.Color = Color.Lerp(limb.InitialLightSourceColor, Color.TransparentBlack, deathAnimTimer / deathAnimDuration);
                    if (limb.InitialLightSpriteAlpha.HasValue)
                    {
                        limb.LightSource.OverrideLightSpriteAlpha = MathHelper.Lerp(limb.InitialLightSpriteAlpha.Value, 0.0f, deathAnimTimer / deathAnimDuration);
                    }
                }
#endif
                if (limb.type == LimbType.Head || limb.type == LimbType.Tail || limb.IsSevered || !limb.body.Enabled)
                {
                    continue;
                }
                if (limb.Mass <= 0.0f)
                {
                    string errorMsg = "Creature death animation error: invalid limb mass on character \"" + character.SpeciesName + "\" (type: " + limb.type + ", mass: " + limb.Mass + ")";
                    DebugConsole.ThrowError(errorMsg);
                    GameAnalyticsManager.AddErrorEventOnce("FishAnimController.UpdateDying:InvalidMass" + character.ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                    deathAnimTimer = deathAnimDuration;
                    return;
                }

                Vector2 diff = (centerOfMass - limb.SimPosition);
                if (!MathUtils.IsValid(diff))
                {
                    string errorMsg = "Creature death animation error: invalid diff (center of mass: " + centerOfMass + ", limb position: " + limb.SimPosition + ")";
                    DebugConsole.ThrowError(errorMsg);
                    GameAnalyticsManager.AddErrorEventOnce("FishAnimController.UpdateDying:InvalidDiff" + character.ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                    deathAnimTimer = deathAnimDuration;
                    return;
                }

                limb.body.ApplyForce(diff * (float)(Math.Sin(WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength, maxVelocity: 10.0f);
            }
        }
Exemplo n.º 7
0
        private void SetDamage(int sectionIndex, float damage, Character attacker = null, bool createNetworkEvent = true)
        {
            if (Submarine != null && Submarine.GodMode)
            {
                return;
            }
            if (!prefab.Body)
            {
                return;
            }
            if (!MathUtils.IsValid(damage))
            {
                return;
            }

            damage = MathHelper.Clamp(damage, 0.0f, prefab.Health);

            if (GameMain.Server != null && createNetworkEvent && damage != sections[sectionIndex].damage)
            {
                GameMain.Server.CreateEntityEvent(this);
            }

            bool noGaps = true;

            for (int i = 0; i < sections.Length; i++)
            {
                if (i != sectionIndex && SectionIsLeaking(i))
                {
                    noGaps = false;
                    break;
                }
            }

            if (damage < prefab.Health * LeakThreshold)
            {
                if (sections[sectionIndex].gap != null)
                {
                    //the structure doesn't have any other gap, log the structure being fixed
                    if (noGaps && attacker != null)
                    {
                        GameServer.Log((sections[sectionIndex].gap.IsRoomToRoom ? "Inner" : "Outer") + " wall repaired by " + attacker.Name, ServerLog.MessageType.ItemInteraction);
                    }

                    DebugConsole.Log("Removing gap (ID " + sections[sectionIndex].gap.ID + ", section: " + sectionIndex + ") from wall " + ID);
                    //remove existing gap if damage is below leak threshold
                    sections[sectionIndex].gap.Open = 0.0f;
                    sections[sectionIndex].gap.Remove();
                    sections[sectionIndex].gap = null;
#if CLIENT
                    if (CastShadow)
                    {
                        GenerateConvexHull();
                    }
#endif
                }
            }
            else
            {
                if (sections[sectionIndex].gap == null)
                {
                    Rectangle gapRect = sections[sectionIndex].rect;
                    gapRect.X                 -= 10;
                    gapRect.Y                 += 10;
                    gapRect.Width             += 20;
                    gapRect.Height            += 20;
                    sections[sectionIndex].gap = new Gap(gapRect, !isHorizontal, Submarine);
                    //free the ID, because if we give gaps IDs we have to make sure they always match between the clients and the server and
                    //that clients create them in the correct order along with every other entity created/removed during the round
                    //which COULD be done via entityspawner, but it's unnecessary because we never access these gaps by ID
                    sections[sectionIndex].gap.FreeID();
                    sections[sectionIndex].gap.ShouldBeSaved = false;
                    sections[sectionIndex].gap.ConnectedWall = this;
                    DebugConsole.Log("Created gap (ID " + sections[sectionIndex].gap.ID + ", section: " + sectionIndex + ") on wall " + ID);
                    //AdjustKarma(attacker, 300);

                    //the structure didn't have any other gaps yet, log the breach
                    if (noGaps && attacker != null)
                    {
                        GameServer.Log((sections[sectionIndex].gap.IsRoomToRoom ? "Inner" : "Outer") + " wall breached by " + attacker.Name, ServerLog.MessageType.ItemInteraction);
                    }
#if CLIENT
                    if (CastShadow)
                    {
                        GenerateConvexHull();
                    }
#endif
                }

                float gapOpen = (damage / prefab.Health - LeakThreshold) * (1.0f / (1.0f - LeakThreshold));
                sections[sectionIndex].gap.Open = gapOpen;
            }

            float damageDiff = damage - sections[sectionIndex].damage;
            bool  hadHole    = SectionBodyDisabled(sectionIndex);
            sections[sectionIndex].damage = MathHelper.Clamp(damage, 0.0f, prefab.Health);

            if (damageDiff != 0.0f) //otherwise it's possible to infinitely gain karma by welding fixed things
            {
                AdjustKarma(attacker, damageDiff);
            }

            bool hasHole = SectionBodyDisabled(sectionIndex);

            if (hadHole == hasHole)
            {
                return;
            }

            UpdateSections();
        }
Exemplo n.º 8
0
        public static void ApplyExplosionForces(Vector2 worldPosition, Attack attack, float force)
        {
            if (attack.Range <= 0.0f)
            {
                return;
            }

            foreach (Character c in Character.CharacterList)
            {
                Vector2 explosionPos = worldPosition;
                if (c.Submarine != null)
                {
                    explosionPos -= c.Submarine.Position;
                }

                explosionPos = ConvertUnits.ToSimUnits(explosionPos);

                Dictionary <Limb, float> distFactors = new Dictionary <Limb, float>();
                foreach (Limb limb in c.AnimController.Limbs)
                {
                    float dist = Vector2.Distance(limb.WorldPosition, worldPosition);

                    //calculate distance from the "outer surface" of the physics body
                    //doesn't take the rotation of the limb into account, but should be accurate enough for this purpose
                    float limbRadius = Math.Max(Math.Max(limb.body.width * 0.5f, limb.body.height * 0.5f), limb.body.radius);
                    dist = Math.Max(0.0f, dist - FarseerPhysics.ConvertUnits.ToDisplayUnits(limbRadius));

                    if (dist > attack.Range)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - dist / attack.Range;

                    //solid obstacles between the explosion and the limb reduce the effect of the explosion by 90%
                    if (Submarine.CheckVisibility(limb.SimPosition, explosionPos) != null)
                    {
                        distFactor *= 0.1f;
                    }

                    distFactors.Add(limb, distFactor);

                    c.AddDamage(limb.WorldPosition, DamageType.None,
                                attack.GetDamage(1.0f) / c.AnimController.Limbs.Length * distFactor,
                                attack.GetBleedingDamage(1.0f) / c.AnimController.Limbs.Length * distFactor,
                                attack.Stun * distFactor,
                                false);

                    if (limb.WorldPosition != worldPosition && force > 0.0f)
                    {
                        Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition);
                        if (!MathUtils.IsValid(limbDiff))
                        {
                            limbDiff = Rand.Vector(1.0f);
                        }
                        Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius;
                        limb.body.ApplyLinearImpulse(limbDiff * distFactor * force, impulsePoint);
                    }
                }

                //sever joints
                if (c.IsDead && attack.SeverLimbsProbability > 0.0f)
                {
                    foreach (Limb limb in c.AnimController.Limbs)
                    {
                        if (!distFactors.ContainsKey(limb))
                        {
                            continue;
                        }

                        foreach (LimbJoint joint in c.AnimController.LimbJoints)
                        {
                            if (joint.IsSevered || (joint.LimbA != limb && joint.LimbB != limb))
                            {
                                continue;
                            }

                            if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability * distFactors[limb])
                            {
                                c.AnimController.SeverLimbJoint(joint);
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 9
0
        private IEnumerable <object> UpdateTransitionCinematic(List <Submarine> subs, Camera cam, Vector2 targetPos)
        {
            if (!subs.Any())
            {
                yield return(CoroutineStatus.Success);
            }

            Character.Controlled = null;
            cam.TargetPos        = Vector2.Zero;
#if CLIENT
            GameMain.LightManager.LosEnabled = false;
#endif

            //Vector2 diff = targetPos - sub.Position;
            float targetSpeed = 10.0f;

            Level.Loaded.TopBarrier.Enabled = false;

            cam.TargetPos = Vector2.Zero;
            float timer = 0.0f;

            while (timer < duration)
            {
                if (Screen.Selected != GameMain.GameScreen)
                {
                    yield return(new WaitForSeconds(0.1f));

#if CLIENT
                    GUI.ScreenOverlayColor = Color.TransparentBlack;
#endif

                    Running = false;
                    yield return(CoroutineStatus.Success);
                }

                cam.Zoom = Math.Max(0.2f, cam.Zoom - CoroutineManager.UnscaledDeltaTime * 0.1f);

                Vector2 cameraPos = subs.First().Position + Submarine.MainSub.HiddenSubPosition;
                cameraPos.Y = Math.Min(cameraPos.Y, ConvertUnits.ToDisplayUnits(Level.Loaded.TopBarrier.Position.Y) - cam.WorldView.Height / 2.0f);
                cam.Translate((cameraPos - cam.Position) * CoroutineManager.UnscaledDeltaTime * 10.0f);
#if CLIENT
                GUI.ScreenOverlayColor = Color.Lerp(Color.TransparentBlack, Color.Black, timer / duration);
#endif

                foreach (Submarine sub in subs)
                {
                    if (sub.Position == targetPos)
                    {
                        continue;
                    }
                    Vector2 dir = Vector2.Normalize(targetPos - sub.Position);
                    if (!MathUtils.IsValid(dir))
                    {
                        continue;
                    }
                    sub.ApplyForce((dir * targetSpeed - sub.Velocity) * 500.0f);
                }

                timer += CoroutineManager.UnscaledDeltaTime;

                yield return(CoroutineStatus.Running);
            }

            Running = false;

            yield return(new WaitForSeconds(0.1f));

#if CLIENT
            GUI.ScreenOverlayColor = Color.TransparentBlack;
#endif

            yield return(CoroutineStatus.Success);
        }
Exemplo n.º 10
0
        private void ApplyImpact(float impact, Vector2 direction, Contact contact, bool applyDamage = true)
        {
            float minImpact = 3.0f;

            if (impact < minImpact)
            {
                return;
            }

            contact.GetWorldManifold(out Vector2 tempNormal, out FixedArray2 <Vector2> worldPoints);
            Vector2 lastContactPoint = worldPoints[0];

            Vector2 impulse = direction * impact * 0.5f;

            impulse = impulse.ClampLength(5.0f);

            if (!MathUtils.IsValid(impulse))
            {
                string errorMsg =
                    "Invalid impulse in SubmarineBody.ApplyImpact: " + impulse +
                    ". Direction: " + direction + ", body position: " + Body.SimPosition + ", impact: " + impact + ".";
                if (GameMain.NetworkMember != null)
                {
                    errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server.";
                }
                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.ThrowError(errorMsg);
                }
                GameAnalyticsManager.AddErrorEventOnce(
                    "SubmarineBody.ApplyImpact:InvalidImpulse",
                    GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                    errorMsg);
                return;
            }

#if CLIENT
            if (Character.Controlled != null && Character.Controlled.Submarine == submarine)
            {
                GameMain.GameScreen.Cam.Shake = impact * 2.0f;
                float angularVelocity =
                    (lastContactPoint.X - Body.SimPosition.X) / ConvertUnits.ToSimUnits(submarine.Borders.Width / 2) * impulse.Y
                    - (lastContactPoint.Y - Body.SimPosition.Y) / ConvertUnits.ToSimUnits(submarine.Borders.Height / 2) * impulse.X;
                GameMain.GameScreen.Cam.AngularVelocity = MathHelper.Clamp(angularVelocity * 0.1f, -1.0f, 1.0f);
            }
#endif

            foreach (Character c in Character.CharacterList)
            {
                if (c.Submarine != submarine)
                {
                    continue;
                }
                if (impact > 2.0f)
                {
                    c.SetStun((impact - 2.0f) * 0.1f);
                }

                foreach (Limb limb in c.AnimController.Limbs)
                {
                    limb.body.ApplyLinearImpulse(limb.Mass * impulse, 20.0f);
                }
                c.AnimController.Collider.ApplyLinearImpulse(c.AnimController.Collider.Mass * impulse, 20.0f);
            }

            foreach (Item item in Item.ItemList)
            {
                if (item.Submarine != submarine || item.CurrentHull == null ||
                    item.body == null || !item.body.Enabled)
                {
                    continue;
                }

                item.body.ApplyLinearImpulse(item.body.Mass * impulse, 20.0f);
            }

            var damagedStructures = Explosion.RangedStructureDamage(
                ConvertUnits.ToDisplayUnits(lastContactPoint),
                impact * 50.0f,
                applyDamage ? impact * ImpactDamageMultiplier : 0.0f);

#if CLIENT
            //play a damage sound for the structure that took the most damage
            float     maxDamage          = 0.0f;
            Structure maxDamageStructure = null;
            foreach (KeyValuePair <Structure, float> structureDamage in damagedStructures)
            {
                if (maxDamageStructure == null || structureDamage.Value > maxDamage)
                {
                    maxDamage          = structureDamage.Value;
                    maxDamageStructure = structureDamage.Key;
                }
            }

            if (maxDamageStructure != null)
            {
                SoundPlayer.PlayDamageSound(
                    "StructureBlunt",
                    impact * 10.0f,
                    ConvertUnits.ToDisplayUnits(lastContactPoint),
                    MathHelper.Lerp(2000.0f, 10000.0f, (impact - minImpact) / 2.0f),
                    maxDamageStructure.Tags);
            }
#endif
        }
Exemplo n.º 11
0
        public static void DamageCharacters(Vector2 worldPosition, Attack attack, float force, Entity damageSource)
        {
            if (attack.Range <= 0.0f)
            {
                return;
            }

            //long range for the broad distance check, because large characters may still be in range even if their collider isn't
            float broadRange = Math.Max(attack.Range * 10.0f, 10000.0f);

            foreach (Character c in Character.CharacterList)
            {
                if (!c.Enabled ||
                    Math.Abs(c.WorldPosition.X - worldPosition.X) > broadRange ||
                    Math.Abs(c.WorldPosition.Y - worldPosition.Y) > broadRange)
                {
                    continue;
                }

                Vector2 explosionPos = worldPosition;
                if (c.Submarine != null)
                {
                    explosionPos -= c.Submarine.Position;
                }

                Hull hull       = Hull.FindHull(ConvertUnits.ToDisplayUnits(explosionPos), null, false);
                bool underWater = hull == null || explosionPos.Y < hull.Surface;

                explosionPos = ConvertUnits.ToSimUnits(explosionPos);

                Dictionary <Limb, float> distFactors = new Dictionary <Limb, float>();
                foreach (Limb limb in c.AnimController.Limbs)
                {
                    float dist = Vector2.Distance(limb.WorldPosition, worldPosition);

                    //calculate distance from the "outer surface" of the physics body
                    //doesn't take the rotation of the limb into account, but should be accurate enough for this purpose
                    float limbRadius = Math.Max(Math.Max(limb.body.width * 0.5f, limb.body.height * 0.5f), limb.body.radius);
                    dist = Math.Max(0.0f, dist - FarseerPhysics.ConvertUnits.ToDisplayUnits(limbRadius));

                    if (dist > attack.Range)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - dist / attack.Range;

                    //solid obstacles between the explosion and the limb reduce the effect of the explosion by 90%
                    if (Submarine.CheckVisibility(limb.SimPosition, explosionPos) != null)
                    {
                        distFactor *= 0.1f;
                    }

                    distFactors.Add(limb, distFactor);

                    List <Affliction> modifiedAfflictions = new List <Affliction>();
                    foreach (Affliction affliction in attack.Afflictions)
                    {
                        modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / c.AnimController.Limbs.Length));
                    }
                    c.LastDamageSource = damageSource;
                    Character attacker = null;
                    if (damageSource is Item item)
                    {
                        attacker = item.GetComponent <Projectile>()?.User;
                        if (attacker == null)
                        {
                            attacker = item.GetComponent <MeleeWeapon>()?.User;
                        }
                    }
                    c.AddDamage(limb.WorldPosition, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);

                    if (attack.StatusEffects != null && attack.StatusEffects.Any())
                    {
                        attack.SetUser(attacker);
                        var statusEffectTargets = new List <ISerializableEntity>()
                        {
                            c, limb
                        };
                        foreach (StatusEffect statusEffect in attack.StatusEffects)
                        {
                            statusEffect.Apply(ActionType.OnUse, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(ActionType.Always, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(underWater ? ActionType.InWater : ActionType.NotInWater, 1.0f, damageSource, statusEffectTargets);
                        }
                    }

                    if (limb.WorldPosition != worldPosition && force > 0.0f)
                    {
                        Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition);
                        if (!MathUtils.IsValid(limbDiff))
                        {
                            limbDiff = Rand.Vector(1.0f);
                        }
                        Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius;
                        limb.body.ApplyLinearImpulse(limbDiff * distFactor * force, impulsePoint);
                    }
                }

                //sever joints
                if (c.IsDead && attack.SeverLimbsProbability > 0.0f)
                {
                    foreach (Limb limb in c.AnimController.Limbs)
                    {
                        if (!distFactors.ContainsKey(limb))
                        {
                            continue;
                        }

                        foreach (LimbJoint joint in c.AnimController.LimbJoints)
                        {
                            if (joint.IsSevered || (joint.LimbA != limb && joint.LimbB != limb))
                            {
                                continue;
                            }

                            if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability * distFactors[limb])
                            {
                                c.AnimController.SeverLimbJoint(joint);
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 12
0
        partial void ClientUpdatePosition(float deltaTime)
        {
            if (GameMain.Client == null)
            {
                return;
            }

            Body.CorrectPosition(positionBuffer, out Vector2 newPosition, out Vector2 newVelocity, out _, out _);
            Vector2 moveAmount = ConvertUnits.ToDisplayUnits(newPosition - Body.SimPosition);

            newVelocity = newVelocity.ClampLength(100.0f);
            if (!MathUtils.IsValid(newVelocity) || moveAmount.LengthSquared() < 0.0001f)
            {
                return;
            }

            List <Submarine> subsToMove = submarine.GetConnectedSubs();

            foreach (Submarine dockedSub in subsToMove)
            {
                if (dockedSub == submarine)
                {
                    continue;
                }
                //clear the position buffer of the docked subs to prevent unnecessary position corrections
                dockedSub.SubBody.positionBuffer.Clear();
            }

            Submarine closestSub;

            if (Character.Controlled == null)
            {
                closestSub = Submarine.FindClosest(GameMain.GameScreen.Cam.Position);
            }
            else
            {
                closestSub = Character.Controlled.Submarine;
            }

            bool displace = moveAmount.LengthSquared() > 100.0f * 100.0f;

            foreach (Submarine sub in subsToMove)
            {
                sub.PhysicsBody.LinearVelocity = newVelocity;

                if (displace)
                {
                    sub.PhysicsBody.SetTransform(sub.PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(moveAmount), 0.0f);
                    sub.SubBody.DisplaceCharacters(moveAmount);
                }
                else
                {
                    sub.PhysicsBody.SetTransformIgnoreContacts(sub.PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(moveAmount), 0.0f);
                }
            }

            if (closestSub != null && subsToMove.Contains(closestSub))
            {
                GameMain.GameScreen.Cam.Position += moveAmount;
                if (GameMain.GameScreen.Cam.TargetPos != Vector2.Zero)
                {
                    GameMain.GameScreen.Cam.TargetPos += moveAmount;
                }

                if (Character.Controlled != null)
                {
                    Character.Controlled.CursorPosition += moveAmount;
                }
            }
        }
        void UpdateDying(float deltaTime)
        {
            if (deathAnimDuration <= 0.0f)
            {
                return;
            }

            float noise        = (PerlinNoise.GetPerlin(WalkPos * 0.002f, WalkPos * 0.003f) - 0.5f) * 5.0f;
            float animStrength = (1.0f - deathAnimTimer / deathAnimDuration);

            Limb baseLimb = GetLimb(LimbType.Head);

            //if head is the main limb, it technically can't be severed - the rest of the limbs are considered severed if the head gets cut off
            if (baseLimb == MainLimb)
            {
                int connectedToHeadCount = GetConnectedLimbs(baseLimb).Count;
                //if there's nothing connected to the head, don't make it wiggle by itself
                if (connectedToHeadCount == 1)
                {
                    baseLimb = null;
                }
                Limb torso = GetLimb(LimbType.Torso, excludeSevered: false);
                if (torso != null)
                {
                    //if there are more limbs connected to the torso than to the head, make the torso wiggle instead
                    int connectedToTorsoCount = GetConnectedLimbs(torso).Count;
                    if (connectedToTorsoCount > connectedToHeadCount)
                    {
                        baseLimb = torso;
                    }
                }
            }
            else if (baseLimb == null)
            {
                baseLimb = GetLimb(LimbType.Torso, excludeSevered: true);
                if (baseLimb == null)
                {
                    return;
                }
            }

            var connectedToBaseLimb = GetConnectedLimbs(baseLimb);

            Limb tail = GetLimb(LimbType.Tail);

            if (baseLimb != null)
            {
                baseLimb.body.ApplyTorque((float)(Math.Sqrt(baseLimb.Mass) * Dir * (Math.Sin(WalkPos) + noise)) * 30.0f * animStrength);
            }
            if (tail != null && connectedToBaseLimb.Contains(tail))
            {
                tail.body.ApplyTorque((float)(Math.Sqrt(tail.Mass) * -Dir * (Math.Sin(WalkPos) + noise)) * 30.0f * animStrength);
            }

            WalkPos += deltaTime * 10.0f * animStrength;

            Vector2 centerOfMass = GetCenterOfMass();

            foreach (Limb limb in Limbs)
            {
                if (!connectedToBaseLimb.Contains(limb))
                {
                    continue;
                }
#if CLIENT
                if (limb.LightSource != null)
                {
                    limb.LightSource.Color = Color.Lerp(limb.InitialLightSourceColor, Color.TransparentBlack, deathAnimTimer / deathAnimDuration);
                    if (limb.InitialLightSpriteAlpha.HasValue)
                    {
                        limb.LightSource.OverrideLightSpriteAlpha = MathHelper.Lerp(limb.InitialLightSpriteAlpha.Value, 0.0f, deathAnimTimer / deathAnimDuration);
                    }
                }
#endif
                if (limb.type == LimbType.Head || limb.type == LimbType.Tail || limb.IsSevered || !limb.body.Enabled)
                {
                    continue;
                }
                if (limb.Mass <= 0.0f)
                {
                    string errorMsg = "Creature death animation error: invalid limb mass on character \"" + character.SpeciesName + "\" (type: " + limb.type + ", mass: " + limb.Mass + ")";
                    DebugConsole.ThrowError(errorMsg);
                    GameAnalyticsManager.AddErrorEventOnce("FishAnimController.UpdateDying:InvalidMass" + character.ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                    deathAnimTimer = deathAnimDuration;
                    return;
                }

                Vector2 diff = (centerOfMass - limb.SimPosition);
                if (!MathUtils.IsValid(diff))
                {
                    string errorMsg = "Creature death animation error: invalid diff (center of mass: " + centerOfMass + ", limb position: " + limb.SimPosition + ")";
                    DebugConsole.ThrowError(errorMsg);
                    GameAnalyticsManager.AddErrorEventOnce("FishAnimController.UpdateDying:InvalidDiff" + character.ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                    deathAnimTimer = deathAnimDuration;
                    return;
                }

                limb.body.ApplyForce(diff * (float)(Math.Sin(WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength, maxVelocity: 10.0f);
            }
        }
Exemplo n.º 14
0
        public static VertexPositionTexture[] GenerateWallShapes(List <VoronoiCell> cells, Level level)
        {
            float inwardThickness = 500.0f, outWardThickness = 30.0f;

            List <VertexPositionTexture> verticeList = new List <VertexPositionTexture>();

            foreach (VoronoiCell cell in cells)
            {
                //if (cell.body == null) continue;
                foreach (GraphEdge edge in cell.edges)
                {
                    if (edge.cell1 != null && edge.cell1.body == null && edge.cell1.CellType != CellType.Empty)
                    {
                        edge.cell1 = null;
                    }
                    if (edge.cell2 != null && edge.cell2.body == null && edge.cell2.CellType != CellType.Empty)
                    {
                        edge.cell2 = null;
                    }

                    CompareCCW compare = new CompareCCW(cell.Center);
                    if (compare.Compare(edge.point1, edge.point2) == -1)
                    {
                        var temp = edge.point1;
                        edge.point1 = edge.point2;
                        edge.point2 = temp;
                    }
                }
            }

            foreach (VoronoiCell cell in cells)
            {
                //if (cell.body == null) continue;
                foreach (GraphEdge edge in cell.edges)
                {
                    if (!edge.isSolid)
                    {
                        continue;
                    }

                    GraphEdge leftEdge  = cell.edges.Find(e => e != edge && (edge.point1 == e.point1 || edge.point1 == e.point2));
                    GraphEdge rightEdge = cell.edges.Find(e => e != edge && (edge.point2 == e.point1 || edge.point2 == e.point2));

                    Vector2 leftNormal = Vector2.Zero, rightNormal = Vector2.Zero;

                    if (leftEdge == null)
                    {
                        leftNormal = GetEdgeNormal(edge, cell);
                    }
                    else
                    {
                        leftNormal = (leftEdge.isSolid) ?
                                     Vector2.Normalize(GetEdgeNormal(leftEdge) + GetEdgeNormal(edge, cell)) :
                                     Vector2.Normalize(leftEdge.Center - edge.point1);
                    }


                    if (!MathUtils.IsValid(leftNormal))
                    {
#if DEBUG
                        DebugConsole.ThrowError("Invalid left normal");
#endif
                        GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidLeftNormal:" + level.Seed,
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
                                                               "Invalid left normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + leftNormal + ", seed: " + level.Seed + ")");

                        if (cell.body != null)
                        {
                            GameMain.World.RemoveBody(cell.body);
                            cell.body = null;
                        }
                        leftNormal = Vector2.UnitX;
                        break;
                    }


                    if (rightEdge == null)
                    {
                        rightNormal = GetEdgeNormal(edge, cell);
                    }
                    else
                    {
                        rightNormal = (rightEdge.isSolid) ?
                                      Vector2.Normalize(GetEdgeNormal(rightEdge) + GetEdgeNormal(edge, cell)) :
                                      Vector2.Normalize(rightEdge.Center - edge.point2);
                    }

                    if (!MathUtils.IsValid(rightNormal))
                    {
#if DEBUG
                        DebugConsole.ThrowError("Invalid right normal");
#endif
                        GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidRightNormal:" + level.Seed,
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
                                                               "Invalid right normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + rightNormal + ", seed: " + level.Seed + ")");

                        if (cell.body != null)
                        {
                            GameMain.World.RemoveBody(cell.body);
                            cell.body = null;
                        }
                        rightNormal = Vector2.UnitX;
                        break;
                    }

                    for (int i = 0; i < 2; i++)
                    {
                        Vector2[] verts = new Vector2[3];
                        VertexPositionTexture[] vertPos = new VertexPositionTexture[3];


                        if (i == 0)
                        {
                            verts[0] = edge.point1 - leftNormal * outWardThickness;
                            verts[1] = edge.point2 - rightNormal * outWardThickness;
                            verts[2] = edge.point1 + leftNormal * inwardThickness;

                            vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), Vector2.Zero);
                            vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), Vector2.UnitX);
                            vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(0, 0.5f));
                        }
                        else
                        {
                            verts[0] = edge.point1 + leftNormal * inwardThickness;
                            verts[1] = edge.point2 - rightNormal * outWardThickness;
                            verts[2] = edge.point2 + rightNormal * inwardThickness;

                            vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), new Vector2(0.0f, 0.5f));
                            vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), Vector2.UnitX);
                            vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(1.0f, 0.5f));
                        }

                        var comparer = new CompareCCW((verts[0] + verts[1] + verts[2]) / 3.0f);
                        Array.Sort(verts, vertPos, comparer);

                        for (int j = 0; j < 3; j++)
                        {
                            verticeList.Add(vertPos[j]);
                        }
                    }
                }
            }

            return(verticeList.ToArray());
        }
Exemplo n.º 15
0
        public void ClientReadPosition(ServerNetObject type, NetBuffer msg, float sendingTime)
        {
            Vector2 newPosition = new Vector2(msg.ReadFloat(), msg.ReadFloat());
            float   newRotation = msg.ReadRangedSingle(0.0f, MathHelper.TwoPi, 7);
            bool    awake       = msg.ReadBoolean();
            Vector2 newVelocity = Vector2.Zero;

            if (awake)
            {
                newVelocity = new Vector2(
                    msg.ReadRangedSingle(-MaxVel, MaxVel, 12),
                    msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
            }

            if (!MathUtils.IsValid(newPosition) || !MathUtils.IsValid(newRotation) || !MathUtils.IsValid(newVelocity))
            {
                string errorMsg = "Received invalid position data for the item \"" + Name
                                  + "\" (position: " + newPosition + ", rotation: " + newRotation + ", velocity: " + newVelocity + ")";
#if DEBUG
                DebugConsole.ThrowError(errorMsg);
#endif
                GameAnalyticsManager.AddErrorEventOnce("Item.ClientReadPosition:InvalidData" + ID,
                                                       GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                       errorMsg);
                return;
            }

            if (body == null)
            {
                DebugConsole.ThrowError("Received a position update for an item with no physics body (" + Name + ")");
                return;
            }

            body.FarseerBody.Awake = awake;
            if (body.FarseerBody.Awake)
            {
                if ((newVelocity - body.LinearVelocity).LengthSquared() > 8.0f * 8.0f)
                {
                    body.LinearVelocity = newVelocity;
                }
            }
            else
            {
                try
                {
                    body.FarseerBody.Enabled = false;
                }
                catch (Exception e)
                {
                    DebugConsole.ThrowError("Exception in PhysicsBody.Enabled = false (" + body.PhysEnabled + ")", e);
                    if (body.UserData != null)
                    {
                        DebugConsole.NewMessage("PhysicsBody UserData: " + body.UserData.GetType().ToString(), Color.Red);
                    }
                    if (GameMain.World.ContactManager == null)
                    {
                        DebugConsole.NewMessage("ContactManager is null!", Color.Red);
                    }
                    else if (GameMain.World.ContactManager.BroadPhase == null)
                    {
                        DebugConsole.NewMessage("Broadphase is null!", Color.Red);
                    }
                    if (body.FarseerBody.FixtureList == null)
                    {
                        DebugConsole.NewMessage("FixtureList is null!", Color.Red);
                    }
                }
            }

            if ((newPosition - SimPosition).Length() > body.LinearVelocity.Length() * 2.0f)
            {
                if (body.SetTransform(newPosition, newRotation))
                {
                    Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition);
                    rect.X = (int)(displayPos.X - rect.Width / 2.0f);
                    rect.Y = (int)(displayPos.Y + rect.Height / 2.0f);
                }
            }
        }
Exemplo n.º 16
0
        protected override void Draw(SpriteBatch spriteBatch)
        {
            if (!Visible)
            {
                return;
            }

            if (ProgressGetter != null)
            {
                float newSize = MathHelper.Clamp(ProgressGetter(), 0.0f, 1.0f);
                if (!MathUtils.IsValid(newSize))
                {
                    GameAnalyticsManager.AddErrorEventOnce(
                        "GUIProgressBar.Draw:GetProgress",
                        GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                        "ProgressGetter of a GUIProgressBar (" + ProgressGetter.Target.ToString() + " - " + ProgressGetter.Method.ToString() + ") returned an invalid value (" + newSize + ")\n" + Environment.StackTrace);
                }
                else
                {
                    BarSize = newSize;
                }
            }

            var sliderRect = GetSliderRect(barSize);

            slider.RectTransform.AbsoluteOffset = new Point((int)style.Padding.X, (int)style.Padding.Y);
            slider.RectTransform.MaxSize        = new Point(
                (int)(Rect.Width - style.Padding.X + style.Padding.Z),
                (int)(Rect.Height - style.Padding.Y + style.Padding.W));
            frame.Visible  = showFrame;
            slider.Visible = BarSize > 0.0f;

            if (showFrame)
            {
                if (AutoDraw)
                {
                    frame.DrawAuto(spriteBatch);
                }
                else
                {
                    frame.DrawManually(spriteBatch);
                }
            }

            Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;

            if (BarSize <= 1.0f)
            {
                spriteBatch.End();
                spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, sliderRect);
                spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
            }

            Color currColor = GetColor(State);

            slider.Color = currColor;
            if (AutoDraw)
            {
                slider.DrawAuto(spriteBatch);
            }
            else
            {
                slider.DrawManually(spriteBatch);
            }
            //hide the slider, we've already drawn it manually
            frame.Visible  = false;
            slider.Visible = false;
            if (BarSize <= 1.0f)
            {
                spriteBatch.End();
                spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
                spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
            }
        }
Exemplo n.º 17
0
        public void Update(float deltaTime)
        {
            if (GameMain.Client != null)
            {
                if (memPos.Count == 0)
                {
                    return;
                }

                Vector2 newVelocity = Body.LinearVelocity;
                Vector2 newPosition = Body.SimPosition;

                Body.CorrectPosition(memPos, deltaTime, out newVelocity, out newPosition);
                Vector2 moveAmount = ConvertUnits.ToDisplayUnits(newPosition - Body.SimPosition);
                newVelocity = newVelocity.ClampLength(100.0f);
                if (!MathUtils.IsValid(newVelocity))
                {
                    return;
                }

                List <Submarine> subsToMove = submarine.GetConnectedSubs();
                foreach (Submarine dockedSub in subsToMove)
                {
                    if (dockedSub == submarine)
                    {
                        continue;
                    }
                    //clear the position buffer of the docked subs to prevent unnecessary position corrections
                    dockedSub.SubBody.memPos.Clear();
                }

                Submarine closestSub = null;
                if (Character.Controlled == null)
                {
                    closestSub = Submarine.FindClosest(GameMain.GameScreen.Cam.WorldViewCenter);
                }
                else
                {
                    closestSub = Character.Controlled.Submarine;
                }

                bool displace = moveAmount.LengthSquared() > 100.0f * 100.0f;
                foreach (Submarine sub in subsToMove)
                {
                    sub.PhysicsBody.SetTransform(sub.PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(moveAmount), 0.0f);
                    sub.PhysicsBody.LinearVelocity = newVelocity;

                    if (displace)
                    {
                        sub.SubBody.DisplaceCharacters(moveAmount);
                    }
                }

                if (closestSub != null && subsToMove.Contains(closestSub))
                {
                    GameMain.GameScreen.Cam.Position += moveAmount;
                    if (GameMain.GameScreen.Cam.TargetPos != Vector2.Zero)
                    {
                        GameMain.GameScreen.Cam.TargetPos += moveAmount;
                    }

                    if (Character.Controlled != null)
                    {
                        Character.Controlled.CursorPosition += moveAmount;
                    }
                }

                return;
            }

            //if outside left or right edge of the level
            if (Position.X < 0 || Position.X > Level.Loaded.Size.X)
            {
                Rectangle worldBorders = Borders;
                worldBorders.Location += MathUtils.ToPoint(Position);

                //push the sub back below the upper "barrier" of the level
                if (worldBorders.Y > Level.Loaded.Size.Y)
                {
                    Body.LinearVelocity = new Vector2(
                        Body.LinearVelocity.X,
                        Math.Min(Body.LinearVelocity.Y, ConvertUnits.ToSimUnits(Level.Loaded.Size.Y - worldBorders.Y)));
                }
                else if (worldBorders.Y - worldBorders.Height < Level.Loaded.BottomPos)
                {
                    Body.LinearVelocity = new Vector2(
                        Body.LinearVelocity.X,
                        Math.Max(Body.LinearVelocity.Y, ConvertUnits.ToSimUnits(Level.Loaded.BottomPos - (worldBorders.Y - worldBorders.Height))));
                }
            }

            //-------------------------

            Vector2 totalForce = CalculateBuoyancy();

            if (Body.LinearVelocity.LengthSquared() > 0.0001f)
            {
                float dragCoefficient = 0.01f;

                float speedLength = (Body.LinearVelocity == Vector2.Zero) ? 0.0f : Body.LinearVelocity.Length();
                float drag        = speedLength * speedLength * dragCoefficient * Body.Mass;

                totalForce += -Vector2.Normalize(Body.LinearVelocity) * drag;
            }

            ApplyForce(totalForce);

            UpdateDepthDamage(deltaTime);
        }
Exemplo n.º 18
0
        private void SetDamage(int sectionIndex, float damage, Character attacker = null)
        {
            if (Submarine != null && Submarine.GodMode)
            {
                return;
            }
            if (!prefab.Body)
            {
                return;
            }

            if (!MathUtils.IsValid(damage))
            {
                return;
            }

            if (GameMain.Server != null && damage != sections[sectionIndex].damage)
            {
                GameMain.Server.CreateEntityEvent(this);
            }

            bool noGaps = true;

            for (int i = 0; i < sections.Length; i++)
            {
                if (i != sectionIndex && SectionIsLeaking(i))
                {
                    noGaps = false;
                    break;
                }
            }

            if (damage < prefab.Health * LeakThreshold)
            {
                if (sections[sectionIndex].gap != null)
                {
                    //the structure doesn't have any other gap, log the structure being fixed
                    if (noGaps && attacker != null)
                    {
                        GameServer.Log((sections[sectionIndex].gap.IsRoomToRoom ? "Inner" : "Outer") + " wall repaired by " + attacker.Name, ServerLog.MessageType.ItemInteraction);
                    }

                    //remove existing gap if damage is below 50%
                    sections[sectionIndex].gap.Remove();
                    sections[sectionIndex].gap = null;
#if CLIENT
                    if (CastShadow)
                    {
                        GenerateConvexHull();
                    }
#endif
                }
            }
            else
            {
                if (sections[sectionIndex].gap == null)
                {
                    Rectangle gapRect = sections[sectionIndex].rect;
                    gapRect.X                 -= 10;
                    gapRect.Y                 += 10;
                    gapRect.Width             += 20;
                    gapRect.Height            += 20;
                    sections[sectionIndex].gap = new Gap(gapRect, !isHorizontal, Submarine);
                    sections[sectionIndex].gap.ConnectedWall = this;
                    //AdjustKarma(attacker, 300);

                    //the structure didn't have any other gaps yet, log the breach
                    if (noGaps && attacker != null)
                    {
                        GameServer.Log((sections[sectionIndex].gap.IsRoomToRoom ? "Inner" : "Outer") + " wall breached by " + attacker.Name, ServerLog.MessageType.ItemInteraction);
                    }
#if CLIENT
                    if (CastShadow)
                    {
                        GenerateConvexHull();
                    }
#endif
                }

                float gapOpen = (damage / prefab.Health - LeakThreshold) * (1.0f / (1.0f - LeakThreshold));
                sections[sectionIndex].gap.Open = gapOpen;
            }

            float damageDiff = damage - sections[sectionIndex].damage;
            bool  hadHole    = SectionBodyDisabled(sectionIndex);
            sections[sectionIndex].damage = MathHelper.Clamp(damage, 0.0f, prefab.Health);

            if (sections[sectionIndex].damage < prefab.Health) //otherwise it's possible to infinitely gain karma by welding fixed things
            {
                AdjustKarma(attacker, damageDiff);
            }

            bool hasHole = SectionBodyDisabled(sectionIndex);

            if (hadHole == hasHole)
            {
                return;
            }

            UpdateSections();
        }
Exemplo n.º 19
0
        private void HandleLimbCollision(Contact contact, Limb limb)
        {
            if (limb.Mass > 100.0f)
            {
                Vector2 normal = Vector2.DistanceSquared(Body.SimPosition, limb.SimPosition) < 0.0001f ?
                                 Vector2.UnitY :
                                 Vector2.Normalize(Body.SimPosition - limb.SimPosition);

                float impact = Math.Min(Vector2.Dot(Velocity - limb.LinearVelocity, -normal), 50.0f) / 5.0f * Math.Min(limb.Mass / 200.0f, 1);

                ApplyImpact(impact, -normal, contact);
                foreach (Submarine dockedSub in submarine.DockedTo)
                {
                    dockedSub.SubBody.ApplyImpact(impact, -normal, contact);
                }
            }

            //find all contacts between the limb and level walls
            List <Contact> levelContacts = new List <Contact>();
            ContactEdge    contactEdge   = limb.body.FarseerBody.ContactList;

            while (contactEdge.Next != null)
            {
                if (contactEdge.Contact.Enabled &&
                    contactEdge.Other.UserData is VoronoiCell &&
                    contactEdge.Contact.IsTouching)
                {
                    levelContacts.Add(contactEdge.Contact);
                }
                contactEdge = contactEdge.Next;
            }

            if (levelContacts.Count == 0)
            {
                return;
            }

            //if the limb is in contact with the level, apply an artifical impact to prevent the sub from bouncing on top of it
            //not a very realistic way to handle the collisions (makes it seem as if the characters were made of reinforced concrete),
            //but more realistic than bouncing and prevents using characters as "bumpers" that prevent all collision damage

            //TODO: apply impact damage and/or gib the character that got crushed between the sub and the level?
            Vector2 avgContactNormal = Vector2.Zero;

            foreach (Contact levelContact in levelContacts)
            {
                levelContact.GetWorldManifold(out Vector2 contactNormal, out FixedArray2 <Vector2> temp);

                //if the contact normal is pointing from the limb towards the level cell it's touching, flip the normal
                VoronoiCell cell = levelContact.FixtureB.UserData is VoronoiCell ?
                                   ((VoronoiCell)levelContact.FixtureB.UserData) : ((VoronoiCell)levelContact.FixtureA.UserData);

                var cellDiff = ConvertUnits.ToDisplayUnits(limb.body.SimPosition) - cell.Center;
                if (Vector2.Dot(contactNormal, cellDiff) < 0)
                {
                    contactNormal = -contactNormal;
                }

                avgContactNormal += contactNormal;

                //apply impacts at the positions where this sub is touching the limb
                ApplyImpact((Vector2.Dot(-Velocity, contactNormal) / 2.0f) / levelContacts.Count, contactNormal, levelContact);
            }
            avgContactNormal /= levelContacts.Count;

            float contactDot = Vector2.Dot(Body.LinearVelocity, -avgContactNormal);

            if (contactDot > 0.001f)
            {
                Vector2 velChange = Vector2.Normalize(Body.LinearVelocity) * contactDot;
                if (!MathUtils.IsValid(velChange))
                {
                    GameAnalyticsManager.AddErrorEventOnce(
                        "SubmarineBody.HandleLimbCollision:" + submarine.ID,
                        GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                        "Invalid velocity change in SubmarineBody.HandleLimbCollision (submarine velocity: " + Body.LinearVelocity
                        + ", avgContactNormal: " + avgContactNormal
                        + ", contactDot: " + contactDot
                        + ", velChange: " + velChange + ")");
                    return;
                }


                Body.LinearVelocity -= velChange;

                float damageAmount = contactDot * Body.Mass / limb.character.Mass;

                Vector2 n;
                FixedArray2 <Vector2> contactPos;
                contact.GetWorldManifold(out n, out contactPos);
                limb.character.DamageLimb(ConvertUnits.ToDisplayUnits(contactPos[0]), limb, DamageType.Blunt, damageAmount, 0.0f, 0.0f, true, 0.0f);

                if (limb.character.IsDead)
                {
                    foreach (LimbJoint limbJoint in limb.character.AnimController.LimbJoints)
                    {
                        if (limbJoint.IsSevered || (limbJoint.LimbA != limb && limbJoint.LimbB != limb))
                        {
                            continue;
                        }
                        limb.character.AnimController.SeverLimbJoint(limbJoint);
                    }
                }
            }
        }
Exemplo n.º 20
0
        private void SetDamage(int sectionIndex, float damage)
        {
            if (Submarine != null && Submarine.GodMode)
            {
                return;
            }
            if (!prefab.HasBody)
            {
                return;
            }

            if (!MathUtils.IsValid(damage))
            {
                return;
            }

            if (GameMain.Server != null && damage != sections[sectionIndex].damage)
            {
                GameMain.Server.CreateEntityEvent(this);
            }

            if (damage < prefab.MaxHealth * 0.5f)
            {
                if (sections[sectionIndex].gap != null)
                {
                    //remove existing gap if damage is below 50%
                    sections[sectionIndex].gap.Remove();
                    sections[sectionIndex].gap = null;
#if CLIENT
                    if (CastShadow)
                    {
                        GenerateConvexHull();
                    }
#endif
                }
            }
            else
            {
                if (sections[sectionIndex].gap == null)
                {
                    Rectangle gapRect = sections[sectionIndex].rect;
                    gapRect.X                 -= 10;
                    gapRect.Y                 += 10;
                    gapRect.Width             += 20;
                    gapRect.Height            += 20;
                    sections[sectionIndex].gap = new Gap(gapRect, !isHorizontal, Submarine);
                    sections[sectionIndex].gap.ConnectedWall = this;
#if CLIENT
                    if (CastShadow)
                    {
                        GenerateConvexHull();
                    }
#endif
                }

                sections[sectionIndex].gap.Open = (damage / prefab.MaxHealth - 0.5f) * 2.0f;
            }

            bool hadHole = SectionBodyDisabled(sectionIndex);
            sections[sectionIndex].damage = MathHelper.Clamp(damage, 0.0f, prefab.MaxHealth);

            bool hasHole = SectionBodyDisabled(sectionIndex);

            if (hadHole == hasHole)
            {
                return;
            }
            //if (hasHole) Explosion.ApplyExplosionForces(sections[sectionIndex].gap.WorldPosition, 500.0f, 5.0f, 0.0f, 0.0f);
            UpdateSections();
        }
Exemplo n.º 21
0
        private void ApplyImpact(float impact, Vector2 direction, Vector2 impactPos, bool applyDamage = true)
        {
            if (impact < MinCollisionImpact)
            {
                return;
            }

            Vector2 impulse = direction * impact * 0.5f;

            impulse = impulse.ClampLength(MaxCollisionImpact);

            if (!MathUtils.IsValid(impulse))
            {
                string errorMsg =
                    "Invalid impulse in SubmarineBody.ApplyImpact: " + impulse +
                    ". Direction: " + direction + ", body position: " + Body.SimPosition + ", impact: " + impact + ".";
                if (GameMain.NetworkMember != null)
                {
                    errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server.";
                }
                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.ThrowError(errorMsg);
                }
                GameAnalyticsManager.AddErrorEventOnce(
                    "SubmarineBody.ApplyImpact:InvalidImpulse",
                    GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                    errorMsg);
                return;
            }

#if CLIENT
            if (Character.Controlled != null && Character.Controlled.Submarine == submarine)
            {
                GameMain.GameScreen.Cam.Shake = impact * 2.0f;
                if (submarine.Info.Type == SubmarineType.Player && !submarine.DockedTo.Any(s => s.Info.Type != SubmarineType.Player))
                {
                    float angularVelocity =
                        (impactPos.X - Body.SimPosition.X) / ConvertUnits.ToSimUnits(submarine.Borders.Width / 2) * impulse.Y
                        - (impactPos.Y - Body.SimPosition.Y) / ConvertUnits.ToSimUnits(submarine.Borders.Height / 2) * impulse.X;
                    GameMain.GameScreen.Cam.AngularVelocity = MathHelper.Clamp(angularVelocity * 0.1f, -1.0f, 1.0f);
                }
            }
#endif

            foreach (Character c in Character.CharacterList)
            {
                if (c.Submarine != submarine)
                {
                    continue;
                }

                foreach (Limb limb in c.AnimController.Limbs)
                {
                    if (limb.IsSevered)
                    {
                        continue;
                    }
                    limb.body.ApplyLinearImpulse(limb.Mass * impulse, 10.0f);
                }
                c.AnimController.Collider.ApplyLinearImpulse(c.AnimController.Collider.Mass * impulse, 10.0f);

                bool holdingOntoSomething = false;
                if (c.SelectedConstruction != null)
                {
                    var controller = c.SelectedConstruction.GetComponent <Items.Components.Controller>();
                    holdingOntoSomething = controller != null && controller.LimbPositions.Any();
                }

                //stun for up to 1 second if the impact equal or higher to the maximum impact
                if (impact >= MaxCollisionImpact && !holdingOntoSomething)
                {
                    c.SetStun(Math.Min(impulse.Length() * 0.2f, 1.0f));
                }
            }

            foreach (Item item in Item.ItemList)
            {
                if (item.Submarine != submarine || item.CurrentHull == null ||
                    item.body == null || !item.body.Enabled)
                {
                    continue;
                }

                item.body.ApplyLinearImpulse(item.body.Mass * impulse, 10.0f);
            }

            var damagedStructures = Explosion.RangedStructureDamage(
                ConvertUnits.ToDisplayUnits(impactPos),
                impact * 50.0f,
                applyDamage ? impact * ImpactDamageMultiplier : 0.0f);

#if CLIENT
            //play a damage sound for the structure that took the most damage
            float     maxDamage          = 0.0f;
            Structure maxDamageStructure = null;
            foreach (KeyValuePair <Structure, float> structureDamage in damagedStructures)
            {
                if (maxDamageStructure == null || structureDamage.Value > maxDamage)
                {
                    maxDamage          = structureDamage.Value;
                    maxDamageStructure = structureDamage.Key;
                }
            }

            if (maxDamageStructure != null)
            {
                SoundPlayer.PlayDamageSound(
                    "StructureBlunt",
                    impact * 10.0f,
                    ConvertUnits.ToDisplayUnits(impactPos),
                    MathHelper.Lerp(2000.0f, 10000.0f, (impact - MinCollisionImpact) / 2.0f),
                    maxDamageStructure.Tags);
            }
#endif
        }
Exemplo n.º 22
0
        private static void UpdateWaterAmbience(float ambienceVolume, float deltaTime)
        {
            if (GameMain.SoundManager.Disabled)
            {
                return;
            }

            //how fast the sub is moving, scaled to 0.0 -> 1.0
            float movementSoundVolume = 0.0f;

            float insideSubFactor = 0.0f;

            foreach (Submarine sub in Submarine.Loaded)
            {
                float movementFactor = (sub.Velocity == Vector2.Zero) ? 0.0f : sub.Velocity.Length() / 10.0f;
                movementFactor = MathHelper.Clamp(movementFactor, 0.0f, 1.0f);

                if (Character.Controlled == null || Character.Controlled.Submarine != sub)
                {
                    float dist = Vector2.Distance(GameMain.GameScreen.Cam.WorldViewCenter, sub.WorldPosition);
                    movementFactor /= Math.Max(dist / 1000.0f, 1.0f);
                    insideSubFactor = Math.Max(1.0f / Math.Max(dist / 1000.0f, 1.0f), insideSubFactor);
                }
                else
                {
                    insideSubFactor = 1.0f;
                }

                movementSoundVolume = Math.Max(movementSoundVolume, movementFactor);
                if (!MathUtils.IsValid(movementSoundVolume))
                {
                    string errorMsg = "Failed to update water ambience volume - submarine's movement value invalid (" + movementSoundVolume + ", sub velocity: " + sub.Velocity + ")";
                    DebugConsole.Log(errorMsg);
                    GameAnalyticsManager.AddErrorEventOnce("SoundPlayer.UpdateWaterAmbience:InvalidVolume", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                    movementSoundVolume = 0.0f;
                }
            }

            for (int i = 0; i < 3; i++)
            {
                float volume = 0.0f;
                Sound sound  = null;
                switch (i)
                {
                case 0:
                    volume = ambienceVolume * (1.0f - movementSoundVolume) * insideSubFactor;
                    sound  = waterAmbienceIn;
                    break;

                case 1:
                    volume = ambienceVolume * movementSoundVolume * insideSubFactor;
                    sound  = waterAmbienceMoving;
                    break;

                case 2:
                    volume = 1.0f - insideSubFactor;
                    sound  = waterAmbienceOut;
                    break;
                }

                if ((waterAmbienceChannels[i] == null || !waterAmbienceChannels[i].IsPlaying) && volume > 0.01f)
                {
                    waterAmbienceChannels[i]         = sound.Play(volume, "waterambience");
                    waterAmbienceChannels[i].Looping = true;
                }
                else if (waterAmbienceChannels[i] != null)
                {
                    waterAmbienceChannels[i].Gain += deltaTime * Math.Sign(volume - waterAmbienceChannels[i].Gain);
                    if (waterAmbienceChannels[i].Gain < 0.01f)
                    {
                        waterAmbienceChannels[i].FadeOutAndDispose();
                    }
                }
            }
        }
Exemplo n.º 23
0
        private void DamageCharacters(Vector2 worldPosition, Attack attack, float force, Entity damageSource, Character attacker)
        {
            if (attack.Range <= 0.0f)
            {
                return;
            }

            //long range for the broad distance check, because large characters may still be in range even if their collider isn't
            float broadRange = Math.Max(attack.Range * 10.0f, 10000.0f);

            foreach (Character c in Character.CharacterList)
            {
                if (!c.Enabled ||
                    Math.Abs(c.WorldPosition.X - worldPosition.X) > broadRange ||
                    Math.Abs(c.WorldPosition.Y - worldPosition.Y) > broadRange)
                {
                    continue;
                }
                if (onlyInside && c.Submarine == null)
                {
                    continue;
                }
                else if (onlyOutside && c.Submarine != null)
                {
                    continue;
                }

                Vector2 explosionPos = worldPosition;
                if (c.Submarine != null)
                {
                    explosionPos -= c.Submarine.Position;
                }

                Hull hull       = Hull.FindHull(explosionPos, null, false);
                bool underWater = hull == null || explosionPos.Y < hull.Surface;

                explosionPos = ConvertUnits.ToSimUnits(explosionPos);

                Dictionary <Limb, float> distFactors         = new Dictionary <Limb, float>();
                Dictionary <Limb, float> damages             = new Dictionary <Limb, float>();
                List <Affliction>        modifiedAfflictions = new List <Affliction>();
                foreach (Limb limb in c.AnimController.Limbs)
                {
                    if (limb.IsSevered || limb.IgnoreCollisions || !limb.body.Enabled)
                    {
                        continue;
                    }

                    float dist = Vector2.Distance(limb.WorldPosition, worldPosition);

                    //calculate distance from the "outer surface" of the physics body
                    //doesn't take the rotation of the limb into account, but should be accurate enough for this purpose
                    float limbRadius = limb.body.GetMaxExtent();
                    dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));

                    if (dist > attack.Range)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - dist / attack.Range;

                    //solid obstacles between the explosion and the limb reduce the effect of the explosion
                    if (!ignoreCover)
                    {
                        distFactor *= GetObstacleDamageMultiplier(explosionPos, worldPosition, limb.SimPosition);
                    }
                    distFactors.Add(limb, distFactor);

                    modifiedAfflictions.Clear();
                    foreach (Affliction affliction in attack.Afflictions.Keys)
                    {
                        //previously the damage would be divided by the number of limbs (the intention was to prevent characters with more limbs taking more damage from explosions)
                        //that didn't work well on large characters like molochs and endworms: the explosions tend to only damage one or two of their limbs, and since the characters
                        //have lots of limbs, they tended to only take a fraction of the damage they should

                        //now we just divide by 10, which keeps the damage to normal-sized characters roughly the same as before and fixes the large characters
                        modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / 10));
                    }
                    c.LastDamageSource = damageSource;
                    if (attacker == null)
                    {
                        if (damageSource is Item item)
                        {
                            attacker = item.GetComponent <Projectile>()?.User;
                            if (attacker == null)
                            {
                                attacker = item.GetComponent <MeleeWeapon>()?.User;
                            }
                        }
                    }

                    //use a position slightly from the limb's position towards the explosion
                    //ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods
                    Vector2      dir          = worldPosition - limb.WorldPosition;
                    Vector2      hitPos       = limb.WorldPosition + (dir.LengthSquared() <= 0.001f ? Rand.Vector(1.0f) : Vector2.Normalize(dir)) * 0.01f;
                    AttackResult attackResult = c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
                    damages.Add(limb, attackResult.Damage);

                    if (attack.StatusEffects != null && attack.StatusEffects.Any())
                    {
                        attack.SetUser(attacker);
                        var statusEffectTargets = new List <ISerializableEntity>()
                        {
                            c, limb
                        };
                        foreach (StatusEffect statusEffect in attack.StatusEffects)
                        {
                            statusEffect.Apply(ActionType.OnUse, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(ActionType.Always, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(underWater ? ActionType.InWater : ActionType.NotInWater, 1.0f, damageSource, statusEffectTargets);
                        }
                    }

                    if (limb.WorldPosition != worldPosition && !MathUtils.NearlyEqual(force, 0.0f))
                    {
                        Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition);
                        if (!MathUtils.IsValid(limbDiff))
                        {
                            limbDiff = Rand.Vector(1.0f);
                        }
                        Vector2 impulse      = limbDiff * distFactor * force;
                        Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius;
                        limb.body.ApplyLinearImpulse(impulse, impulsePoint, maxVelocity: NetConfig.MaxPhysicsBodyVelocity * 0.2f);
                    }
                }

                if (c == Character.Controlled && !c.IsDead && playTinnitus)
                {
                    Limb head = c.AnimController.GetLimb(LimbType.Head);
                    if (head != null && damages.TryGetValue(head, out float headDamage) && headDamage > 0.0f && distFactors.TryGetValue(head, out float headFactor))
                    {
                        PlayTinnitusProjSpecific(headFactor);
                    }
                }

                //sever joints
                if (attack.SeverLimbsProbability > 0.0f)
                {
                    foreach (Limb limb in c.AnimController.Limbs)
                    {
                        if (limb.character.Removed || limb.Removed)
                        {
                            continue;
                        }
                        if (limb.IsSevered)
                        {
                            continue;
                        }
                        if (!c.IsDead && !limb.CanBeSeveredAlive)
                        {
                            continue;
                        }
                        if (distFactors.TryGetValue(limb, out float distFactor))
                        {
                            if (damages.TryGetValue(limb, out float damage))
                            {
                                c.TrySeverLimbJoints(limb, attack.SeverLimbsProbability * distFactor, damage, allowBeheading: true);
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 24
0
        private void HandleLimbCollision(Impact collision, Limb limb)
        {
            if (limb?.body?.FarseerBody == null || limb.character == null)
            {
                return;
            }

            float impactMass       = limb.Mass;
            var   enemyAI          = limb.character.AIController as EnemyAIController;
            float attackMultiplier = 1.0f;

            if (enemyAI?.ActiveAttack != null)
            {
                impactMass       = Math.Max(Math.Max(limb.Mass, limb.character.AnimController.MainLimb.Mass), limb.character.AnimController.Collider.Mass);
                attackMultiplier = enemyAI.ActiveAttack.SubmarineImpactMultiplier;
            }

            if (impactMass * attackMultiplier > MinImpactLimbMass)
            {
                Vector2 normal =
                    Vector2.DistanceSquared(Body.SimPosition, limb.SimPosition) < 0.0001f ?
                    Vector2.UnitY :
                    Vector2.Normalize(Body.SimPosition - limb.SimPosition);

                float impact = Math.Min(Vector2.Dot(collision.Velocity, -normal), 50.0f) * Math.Min(impactMass / 300.0f, 1);
                impact *= attackMultiplier;

                ApplyImpact(impact, normal, collision.ImpactPos, applyDamage: false);
                foreach (Submarine dockedSub in submarine.DockedTo)
                {
                    dockedSub.SubBody.ApplyImpact(impact, normal, collision.ImpactPos, applyDamage: false);
                }
            }

            //find all contacts between the limb and level walls
            List <Contact> levelContacts = new List <Contact>();
            ContactEdge    contactEdge   = limb.body.FarseerBody.ContactList;

            while (contactEdge?.Contact != null)
            {
                if (contactEdge.Contact.Enabled &&
                    contactEdge.Contact.IsTouching &&
                    contactEdge.Other?.UserData is VoronoiCell)
                {
                    levelContacts.Add(contactEdge.Contact);
                }
                contactEdge = contactEdge.Next;
            }

            if (levelContacts.Count == 0)
            {
                return;
            }

            //if the limb is in contact with the level, apply an artifical impact to prevent the sub from bouncing on top of it
            //not a very realistic way to handle the collisions (makes it seem as if the characters were made of reinforced concrete),
            //but more realistic than bouncing and prevents using characters as "bumpers" that prevent all collision damage
            Vector2 avgContactNormal = Vector2.Zero;

            foreach (Contact levelContact in levelContacts)
            {
                levelContact.GetWorldManifold(out Vector2 contactNormal, out FixedArray2 <Vector2> temp);

                //if the contact normal is pointing from the limb towards the level cell it's touching, flip the normal
                VoronoiCell cell = levelContact.FixtureB.UserData is VoronoiCell ?
                                   ((VoronoiCell)levelContact.FixtureB.UserData) : ((VoronoiCell)levelContact.FixtureA.UserData);

                var cellDiff = ConvertUnits.ToDisplayUnits(limb.body.SimPosition) - cell.Center;
                if (Vector2.Dot(contactNormal, cellDiff) < 0)
                {
                    contactNormal = -contactNormal;
                }

                avgContactNormal += contactNormal;

                //apply impacts at the positions where this sub is touching the limb
                ApplyImpact((Vector2.Dot(-collision.Velocity, contactNormal) / 2.0f) / levelContacts.Count, contactNormal, collision.ImpactPos, applyDamage: false);
            }
            avgContactNormal /= levelContacts.Count;

            float contactDot = Vector2.Dot(Body.LinearVelocity, -avgContactNormal);

            if (contactDot > 0.001f)
            {
                Vector2 velChange = Vector2.Normalize(Body.LinearVelocity) * contactDot;
                if (!MathUtils.IsValid(velChange))
                {
                    GameAnalyticsManager.AddErrorEventOnce(
                        "SubmarineBody.HandleLimbCollision:" + submarine.ID,
                        GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                        "Invalid velocity change in SubmarineBody.HandleLimbCollision (submarine velocity: " + Body.LinearVelocity
                        + ", avgContactNormal: " + avgContactNormal
                        + ", contactDot: " + contactDot
                        + ", velChange: " + velChange + ")");
                    return;
                }

                Body.LinearVelocity -= velChange;

                if (contactDot > 0.1f)
                {
                    float damageAmount = contactDot * Body.Mass / limb.character.Mass;
                    limb.character.LastDamageSource = submarine;
                    limb.character.DamageLimb(ConvertUnits.ToDisplayUnits(collision.ImpactPos), limb,
                                              AfflictionPrefab.ImpactDamage.Instantiate(damageAmount).ToEnumerable(), 0.0f, true, 0.0f);

                    if (limb.character.IsDead)
                    {
                        foreach (LimbJoint limbJoint in limb.character.AnimController.LimbJoints)
                        {
                            if (limbJoint.IsSevered || (limbJoint.LimbA != limb && limbJoint.LimbB != limb))
                            {
                                continue;
                            }
                            limb.character.AnimController.SeverLimbJoint(limbJoint);
                        }
                    }
                }
            }
        }
        public static List <VertexPositionTexture> GenerateWallEdgeVertices(List <VoronoiCell> cells, Level level, float zCoord)
        {
            float outWardThickness = level.GenerationParams.WallEdgeExpandOutwardsAmount;

            List <VertexPositionTexture> vertices = new List <VertexPositionTexture>();

            foreach (VoronoiCell cell in cells)
            {
                Vector2 minVert       = cell.Edges[0].Point1;
                Vector2 maxVert       = cell.Edges[0].Point1;
                float   circumference = 0.0f;
                foreach (GraphEdge edge in cell.Edges)
                {
                    circumference += Vector2.Distance(edge.Point1, edge.Point2);
                    minVert        = new Vector2(
                        Math.Min(minVert.X, edge.Point1.X),
                        Math.Min(minVert.Y, edge.Point1.Y));
                    maxVert = new Vector2(
                        Math.Max(maxVert.X, edge.Point1.X),
                        Math.Max(maxVert.Y, edge.Point1.Y));
                }
                Vector2 center = (minVert + maxVert) / 2;
                foreach (GraphEdge edge in cell.Edges)
                {
                    if (!edge.IsSolid)
                    {
                        continue;
                    }

                    GraphEdge leftEdge         = cell.Edges.Find(e => e != edge && (edge.Point1.NearlyEquals(e.Point1) || edge.Point1.NearlyEquals(e.Point2)));
                    var       leftAdjacentCell = leftEdge?.AdjacentCell(cell);
                    if (leftAdjacentCell != null)
                    {
                        var adjEdge = leftAdjacentCell.Edges.Find(e => e != leftEdge && e.IsSolid && (edge.Point1.NearlyEquals(e.Point1) || edge.Point1.NearlyEquals(e.Point2)));
                        if (adjEdge != null)
                        {
                            leftEdge = adjEdge;
                        }
                    }

                    GraphEdge rightEdge         = cell.Edges.Find(e => e != edge && (edge.Point2.NearlyEquals(e.Point1) || edge.Point2.NearlyEquals(e.Point2)));
                    var       rightAdjacentCell = rightEdge?.AdjacentCell(cell);
                    if (rightAdjacentCell != null)
                    {
                        var adjEdge = rightAdjacentCell.Edges.Find(e => e != rightEdge && e.IsSolid && (edge.Point2.NearlyEquals(e.Point1) || edge.Point2.NearlyEquals(e.Point2)));
                        if (adjEdge != null)
                        {
                            rightEdge = adjEdge;
                        }
                    }

                    Vector2 leftNormal = Vector2.Zero, rightNormal = Vector2.Zero;

                    float inwardThickness1 = level.GenerationParams.WallEdgeExpandInwardsAmount;
                    float inwardThickness2 = level.GenerationParams.WallEdgeExpandInwardsAmount;
                    if (leftEdge != null && !leftEdge.IsSolid)
                    {
                        leftNormal = edge.Point1.NearlyEquals(leftEdge.Point1) ?
                                     Vector2.Normalize(leftEdge.Point2 - leftEdge.Point1) :
                                     Vector2.Normalize(leftEdge.Point1 - leftEdge.Point2);
                    }
                    else if (leftEdge != null)
                    {
                        leftNormal = -Vector2.Normalize(edge.GetNormal(cell) + leftEdge.GetNormal(leftAdjacentCell ?? cell));
                        if (!MathUtils.IsValid(leftNormal))
                        {
                            leftNormal = -edge.GetNormal(cell);
                        }
                    }
                    else
                    {
                        leftNormal = Vector2.Normalize(cell.Center - edge.Point1);
                    }
                    inwardThickness1 = Math.Min(Vector2.Distance(edge.Point1, cell.Center), inwardThickness1);

                    if (!MathUtils.IsValid(leftNormal))
                    {
#if DEBUG
                        DebugConsole.ThrowError("Invalid left normal");
#endif
                        GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidLeftNormal:" + level.Seed,
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
                                                               "Invalid left normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + leftNormal + ", seed: " + level.Seed + ")");

                        if (cell.Body != null)
                        {
                            if (GameMain.World.BodyList.Contains(cell.Body))
                            {
                                GameMain.World.Remove(cell.Body);
                            }
                            cell.Body = null;
                        }
                        leftNormal = Vector2.UnitX;
                        break;
                    }

                    if (rightEdge != null && !rightEdge.IsSolid)
                    {
                        rightNormal = edge.Point2.NearlyEquals(rightEdge.Point1) ?
                                      Vector2.Normalize(rightEdge.Point2 - rightEdge.Point1) :
                                      Vector2.Normalize(rightEdge.Point1 - rightEdge.Point2);
                    }
                    else if (rightEdge != null)
                    {
                        rightNormal = -Vector2.Normalize(edge.GetNormal(cell) + rightEdge.GetNormal(rightAdjacentCell ?? cell));
                        if (!MathUtils.IsValid(rightNormal))
                        {
                            rightNormal = -edge.GetNormal(cell);
                        }
                    }
                    else
                    {
                        rightNormal = Vector2.Normalize(cell.Center - edge.Point2);
                    }
                    inwardThickness2 = Math.Min(Vector2.Distance(edge.Point2, cell.Center), inwardThickness2);

                    if (!MathUtils.IsValid(rightNormal))
                    {
#if DEBUG
                        DebugConsole.ThrowError("Invalid right normal");
#endif
                        GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidRightNormal:" + level.Seed,
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
                                                               "Invalid right normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + rightNormal + ", seed: " + level.Seed + ")");

                        if (cell.Body != null)
                        {
                            if (GameMain.World.BodyList.Contains(cell.Body))
                            {
                                GameMain.World.Remove(cell.Body);
                            }
                            cell.Body = null;
                        }
                        rightNormal = Vector2.UnitX;
                        break;
                    }

                    float point1UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point1 - center));
                    float point2UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point2 - center));
                    //handle wrapping around 0/360
                    if (point1UV - point2UV > MathHelper.Pi)
                    {
                        point1UV -= MathHelper.TwoPi;
                    }
                    int textureRepeatCount = (int)Math.Max(circumference / 2 / level.GenerationParams.WallEdgeTextureWidth, 1);
                    point1UV = point1UV / MathHelper.TwoPi * textureRepeatCount;
                    point2UV = point2UV / MathHelper.TwoPi * textureRepeatCount;

                    for (int i = 0; i < 2; i++)
                    {
                        Vector2[] verts = new Vector2[3];
                        VertexPositionTexture[] vertPos = new VertexPositionTexture[3];

                        if (i == 0)
                        {
                            verts[0] = edge.Point1 - leftNormal * outWardThickness;
                            verts[1] = edge.Point2 - rightNormal * outWardThickness;
                            verts[2] = edge.Point1 + leftNormal * inwardThickness1;

                            vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], zCoord), new Vector2(point1UV, 0.0f));
                            vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], zCoord), new Vector2(point2UV, 0.0f));
                            vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], zCoord), new Vector2(point1UV, 1.0f));
                        }
                        else
                        {
                            verts[0] = edge.Point1 + leftNormal * inwardThickness1;
                            verts[1] = edge.Point2 - rightNormal * outWardThickness;
                            verts[2] = edge.Point2 + rightNormal * inwardThickness2;

                            vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], zCoord), new Vector2(point1UV, 1.0f));
                            vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], zCoord), new Vector2(point2UV, 0.0f));
                            vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], zCoord), new Vector2(point2UV, 1.0f));
                        }
                        vertices.AddRange(vertPos);
                    }
                }
            }

            return(vertices);
        }
Exemplo n.º 26
0
        public static void DamageCharacters(Vector2 worldPosition, Attack attack, float force, Entity damageSource, Character attacker)
        {
            if (attack.Range <= 0.0f)
            {
                return;
            }

            //long range for the broad distance check, because large characters may still be in range even if their collider isn't
            float broadRange = Math.Max(attack.Range * 10.0f, 10000.0f);

            foreach (Character c in Character.CharacterList)
            {
                if (!c.Enabled ||
                    Math.Abs(c.WorldPosition.X - worldPosition.X) > broadRange ||
                    Math.Abs(c.WorldPosition.Y - worldPosition.Y) > broadRange)
                {
                    continue;
                }

                Vector2 explosionPos = worldPosition;
                if (c.Submarine != null)
                {
                    explosionPos -= c.Submarine.Position;
                }

                Hull hull       = Hull.FindHull(explosionPos, null, false);
                bool underWater = hull == null || explosionPos.Y < hull.Surface;

                explosionPos = ConvertUnits.ToSimUnits(explosionPos);

                Dictionary <Limb, float> distFactors = new Dictionary <Limb, float>();
                Dictionary <Limb, float> damages     = new Dictionary <Limb, float>();
                foreach (Limb limb in c.AnimController.Limbs)
                {
                    float dist = Vector2.Distance(limb.WorldPosition, worldPosition);

                    //calculate distance from the "outer surface" of the physics body
                    //doesn't take the rotation of the limb into account, but should be accurate enough for this purpose
                    float limbRadius = limb.body.GetMaxExtent();
                    dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));

                    if (dist > attack.Range)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - dist / attack.Range;

                    //solid obstacles between the explosion and the limb reduce the effect of the explosion by 90%
                    if (Submarine.CheckVisibility(limb.SimPosition, explosionPos) != null)
                    {
                        distFactor *= 0.1f;
                    }

                    distFactors.Add(limb, distFactor);

                    List <Affliction> modifiedAfflictions = new List <Affliction>();
                    int limbCount = c.AnimController.Limbs.Count(l => !l.IsSevered && !l.ignoreCollisions);
                    foreach (Affliction affliction in attack.Afflictions.Keys)
                    {
                        modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / limbCount));
                    }
                    c.LastDamageSource = damageSource;
                    if (attacker == null)
                    {
                        if (damageSource is Item item)
                        {
                            attacker = item.GetComponent <Projectile>()?.User;
                            if (attacker == null)
                            {
                                attacker = item.GetComponent <MeleeWeapon>()?.User;
                            }
                        }
                    }

                    //use a position slightly from the limb's position towards the explosion
                    //ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods
                    Vector2      hitPos       = limb.WorldPosition + (worldPosition - limb.WorldPosition) / dist * 0.01f;
                    AttackResult attackResult = c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
                    damages.Add(limb, attackResult.Damage);

                    if (attack.StatusEffects != null && attack.StatusEffects.Any())
                    {
                        attack.SetUser(attacker);
                        var statusEffectTargets = new List <ISerializableEntity>()
                        {
                            c, limb
                        };
                        foreach (StatusEffect statusEffect in attack.StatusEffects)
                        {
                            statusEffect.Apply(ActionType.OnUse, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(ActionType.Always, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(underWater ? ActionType.InWater : ActionType.NotInWater, 1.0f, damageSource, statusEffectTargets);
                        }
                    }

                    if (limb.WorldPosition != worldPosition && !MathUtils.NearlyEqual(force, 0.0f))
                    {
                        Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition);
                        if (!MathUtils.IsValid(limbDiff))
                        {
                            limbDiff = Rand.Vector(1.0f);
                        }
                        Vector2 impulse      = limbDiff * distFactor * force;
                        Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius;
                        limb.body.ApplyLinearImpulse(impulse, impulsePoint, maxVelocity: NetConfig.MaxPhysicsBodyVelocity * 0.2f);
                    }
                }

                //sever joints
                if (attack.SeverLimbsProbability > 0.0f)
                {
                    foreach (Limb limb in c.AnimController.Limbs)
                    {
                        if (limb.character.Removed || limb.Removed)
                        {
                            continue;
                        }
                        if (limb.IsSevered)
                        {
                            continue;
                        }
                        if (!c.IsDead && !limb.CanBeSeveredAlive)
                        {
                            continue;
                        }
                        if (distFactors.TryGetValue(limb, out float distFactor))
                        {
                            if (damages.TryGetValue(limb, out float damage))
                            {
                                c.TrySeverLimbJoints(limb, attack.SeverLimbsProbability * distFactor, damage, allowBeheading: true);
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 27
0
        protected override void Draw(SpriteBatch spriteBatch)
        {
            if (!Visible)
            {
                return;
            }

            if (ProgressGetter != null)
            {
                float newSize = MathHelper.Clamp(ProgressGetter(), 0.0f, 1.0f);
                if (!MathUtils.IsValid(newSize))
                {
                    GameAnalyticsManager.AddErrorEventOnce(
                        "GUIProgressBar.Draw:GetProgress",
                        GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                        "ProgressGetter of a GUIProgressBar (" + ProgressGetter.Target.ToString() + " - " + ProgressGetter.Method.ToString() + ") returned an invalid value (" + newSize + ")\n" + Environment.StackTrace);
                }
                else
                {
                    BarSize = newSize;
                }
            }

            Rectangle sliderRect = new Rectangle(
                frame.Rect.X,
                (int)(frame.Rect.Y + (isHorizontal ? 0 : frame.Rect.Height * (1.0f - barSize))),
                isHorizontal ? (int)((frame.Rect.Width) * barSize) : frame.Rect.Width,
                isHorizontal ? (int)(frame.Rect.Height) : (int)(frame.Rect.Height * barSize));

            frame.Visible  = true;
            slider.Visible = true;
            if (AutoDraw)
            {
                frame.DrawAuto(spriteBatch);
            }
            else
            {
                frame.DrawManually(spriteBatch);
            }

            Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;

            if (BarSize <= 1.0f)
            {
                spriteBatch.End();
                spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, sliderRect);
                spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
            }

            Color currColor = GetCurrentColor(state);

            slider.Color = currColor;
            if (AutoDraw)
            {
                slider.DrawAuto(spriteBatch);
            }
            else
            {
                slider.DrawManually(spriteBatch);
            }
            //hide the slider, we've already drawn it manually
            frame.Visible  = false;
            slider.Visible = false;
            if (BarSize <= 1.0f)
            {
                spriteBatch.End();
                spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
                spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
            }
        }