Example #1
0
        partial void ApplyProjSpecific(float deltaTime, Entity entity, IEnumerable <ISerializableEntity> targets, Hull hull, Vector2 worldPosition, bool playSound)
        {
            if (entity == null)
            {
                return;
            }

            if (sounds.Count > 0 && playSound)
            {
                if (soundChannel == null || !soundChannel.IsPlaying)
                {
                    if (soundSelectionMode == SoundSelectionMode.All)
                    {
                        foreach (RoundSound sound in sounds)
                        {
                            if (sound?.Sound == null)
                            {
                                string errorMsg = $"Error in StatusEffect.ApplyProjSpecific1 (sound \"{sound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace();
                                GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull1" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                                return;
                            }
                            soundChannel = SoundPlayer.PlaySound(sound.Sound, worldPosition, sound.Volume, sound.Range, hullGuess: hull);
                            if (soundChannel != null)
                            {
                                soundChannel.Looping = loopSound;
                            }
                        }
                    }
                    else
                    {
                        int selectedSoundIndex;
                        if (soundSelectionMode == SoundSelectionMode.ItemSpecific && entity is Item item)
                        {
                            selectedSoundIndex = item.ID % sounds.Count;
                        }
                        else if (soundSelectionMode == SoundSelectionMode.CharacterSpecific && entity is Character user)
                        {
                            selectedSoundIndex = user.ID % sounds.Count;
                        }
                        else
                        {
                            selectedSoundIndex = Rand.Int(sounds.Count);
                        }
                        var selectedSound = sounds[selectedSoundIndex];
                        if (selectedSound?.Sound == null)
                        {
                            string errorMsg = $"Error in StatusEffect.ApplyProjSpecific2 (sound \"{selectedSound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace.CleanupStackTrace();
                            GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull2" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                            return;
                        }
                        if (selectedSound.Sound.Disposed)
                        {
                            Submarine.ReloadRoundSound(selectedSound);
                        }
                        soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, worldPosition, selectedSound.Volume, selectedSound.Range, hullGuess: hull);
                        if (soundChannel != null)
                        {
                            soundChannel.Looping = loopSound;
                        }
                    }
                }
                else
                {
                    soundChannel.Position = new Vector3(worldPosition, 0.0f);
                }

                if (soundChannel != null && soundChannel.Looping)
                {
                    ActiveLoopingSounds.Add(this);
                    soundEmitter  = entity;
                    loopStartTime = Timing.TotalTime;
                }
            }

            foreach (ParticleEmitter emitter in particleEmitters)
            {
                float angle            = 0.0f;
                float particleRotation = 0.0f;
                if (emitter.Prefab.CopyEntityAngle)
                {
                    Limb targetLimb = null;
                    if (entity is Item item && item.body != null)
                    {
                        angle            = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
                        particleRotation = -item.body.Rotation;
                        if (item.body.Dir < 0.0f)
                        {
                            particleRotation += MathHelper.Pi;
                        }
                    }
                    else if (entity is Character c && targetLimbs?.FirstOrDefault(l => l != LimbType.None) is LimbType l)
                    {
                        targetLimb = c.AnimController.GetLimb(l);
                    }
        void UpdateWalkAnim(float deltaTime)
        {
            if (CurrentGroundedParams == null)
            {
                return;
            }
            movement = MathUtils.SmoothStep(movement, TargetMovement, 0.2f);

            Collider.LinearVelocity = new Vector2(
                movement.X,
                Collider.LinearVelocity.Y > 0.0f ? Collider.LinearVelocity.Y * 0.5f : Collider.LinearVelocity.Y);

            //limbs are disabled when simple physics is enabled, no need to move them
            if (SimplePhysicsEnabled)
            {
                return;
            }

            float mainLimbHeight = ColliderHeightFromFloor;

            Vector2 colliderBottom = GetColliderBottom();

            float movementAngle = 0.0f;
            float mainLimbAngle = (MainLimb.type == LimbType.Torso ? TorsoAngle ?? 0 : HeadAngle ?? 0) * Dir;

            while (MainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
            {
                movementAngle += MathHelper.TwoPi;
            }
            while (MainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
            {
                movementAngle -= MathHelper.TwoPi;
            }

            Limb torso = GetLimb(LimbType.Torso);

            if (torso != null)
            {
                if (TorsoAngle.HasValue)
                {
                    SmoothRotateWithoutWrapping(torso, movementAngle + TorsoAngle.Value * Dir, MainLimb, TorsoTorque);
                }
                if (TorsoPosition.HasValue)
                {
                    Vector2 pos = colliderBottom + Vector2.UnitY * TorsoPosition.Value;

                    if (torso != MainLimb)
                    {
                        pos.X = torso.SimPosition.X;
                    }
                    else
                    {
                        mainLimbHeight = TorsoPosition.Value;
                    }

                    torso.MoveToPos(pos, TorsoMoveForce);
                    torso.PullJointEnabled      = true;
                    torso.PullJointWorldAnchorB = pos;
                }
            }

            Limb head = GetLimb(LimbType.Head);

            if (head != null)
            {
                if (HeadAngle.HasValue)
                {
                    SmoothRotateWithoutWrapping(head, movementAngle + HeadAngle.Value * Dir, MainLimb, HeadTorque);
                }
                if (HeadPosition.HasValue)
                {
                    Vector2 pos = colliderBottom + Vector2.UnitY * HeadPosition.Value;

                    if (head != MainLimb)
                    {
                        pos.X = head.SimPosition.X;
                    }
                    else
                    {
                        mainLimbHeight = HeadPosition.Value;
                    }

                    head.MoveToPos(pos, HeadMoveForce);
                    head.PullJointEnabled      = true;
                    head.PullJointWorldAnchorB = pos;
                }
            }

            if (TailAngle.HasValue)
            {
                var tail = GetLimb(LimbType.Tail);
                if (tail != null)
                {
                    SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, MainLimb, TailTorque);
                }
            }

            WalkPos -= MainLimb.LinearVelocity.X * (CurrentAnimationParams.CycleSpeed / RagdollParams.JointScale / 100.0f);

            Vector2 transformedStepSize = Vector2.Zero;

            if (Math.Abs(TargetMovement.X) > 0.01f)
            {
                transformedStepSize = new Vector2(
                    (float)Math.Cos(WalkPos) * StepSize.Value.X * 3.0f,
                    (float)Math.Sin(WalkPos) * StepSize.Value.Y * 2.0f);
            }

            foreach (Limb limb in Limbs)
            {
                switch (limb.type)
                {
                case LimbType.LeftFoot:
                case LimbType.RightFoot:
                    Vector2 footPos = new Vector2(limb.SimPosition.X, colliderBottom.Y);

                    if (limb.RefJointIndex > -1)
                    {
                        if (LimbJoints.Length <= limb.RefJointIndex)
                        {
                            DebugConsole.ThrowError($"Reference joint index {limb.RefJointIndex} is out of array. This is probably due to a missing joint. If you just deleted a joint, don't do that without first removing the reference joint indices from the limbs. If this is not the case, please ensure that you have defined the index to the right joint.");
                        }
                        else
                        {
                            footPos.X = LimbJoints[limb.RefJointIndex].WorldAnchorA.X;
                        }
                    }
                    footPos.X += limb.StepOffset.X * Dir;
                    footPos.Y += limb.StepOffset.Y;

                    if (limb.type == LimbType.LeftFoot)
                    {
                        limb.DebugRefPos    = footPos + Vector2.UnitX * movement.X * 0.1f;
                        limb.DebugTargetPos = footPos + new Vector2(
                            transformedStepSize.X + movement.X * 0.1f,
                            (transformedStepSize.Y > 0.0f) ? transformedStepSize.Y : 0.0f);
                        limb.MoveToPos(limb.DebugTargetPos, FootMoveForce);
                    }
                    else if (limb.type == LimbType.RightFoot)
                    {
                        limb.DebugRefPos    = footPos + Vector2.UnitX * movement.X * 0.1f;
                        limb.DebugTargetPos = footPos + new Vector2(
                            -transformedStepSize.X + movement.X * 0.1f,
                            (-transformedStepSize.Y > 0.0f) ? -transformedStepSize.Y : 0.0f);
                        limb.MoveToPos(limb.DebugTargetPos, FootMoveForce);
                    }

                    if (CurrentGroundedParams.FootAnglesInRadians.ContainsKey(limb.limbParams.ID))
                    {
                        SmoothRotateWithoutWrapping(limb,
                                                    movementAngle + CurrentGroundedParams.FootAnglesInRadians[limb.limbParams.ID] * Dir,
                                                    MainLimb, FootTorque);
                    }
                    break;

                case LimbType.LeftLeg:
                case LimbType.RightLeg:
                    if (Math.Abs(CurrentGroundedParams.LegTorque) > 0.001f)
                    {
                        limb.body.ApplyTorque(limb.Mass * CurrentGroundedParams.LegTorque * Dir);
                    }
                    break;
                }
            }
        }
Example #3
0
        protected void Apply(float deltaTime, Entity entity, List <ISerializableEntity> targets)
        {
            Hull hull = null;

            if (entity is Character)
            {
                hull = ((Character)entity).AnimController.CurrentHull;
            }
            else if (entity is Item)
            {
                hull = ((Item)entity).CurrentHull;
            }

            foreach (ISerializableEntity serializableEntity in targets)
            {
                if (!(serializableEntity is Item item))
                {
                    continue;
                }

                Character targetCharacter = targets.FirstOrDefault(t => t is Character character && !character.Removed) as Character;
                if (targetCharacter == null)
                {
                    foreach (var target in targets)
                    {
                        if (target is Limb limb && limb.character != null && !limb.character.Removed)
                        {
                            targetCharacter = ((Limb)target).character;
                        }
                    }
                }
                for (int i = 0; i < useItemCount; i++)
                {
                    if (item.Removed)
                    {
                        continue;
                    }
                    item.Use(deltaTime, targetCharacter, targets.FirstOrDefault(t => t is Limb) as Limb);
                }
            }

            if (removeItem)
            {
                foreach (Item item in targets.Where(t => t is Item).Cast <Item>())
                {
                    Entity.Spawner?.AddToRemoveQueue(item);
                }
            }

            if (duration > 0.0f)
            {
                DurationListElement element = new DurationListElement
                {
                    Parent  = this,
                    Timer   = duration,
                    Entity  = entity,
                    Targets = targets
                };

                DurationList.Add(element);
            }
            else
            {
                foreach (ISerializableEntity target in targets)
                {
                    if (target is Entity targetEntity)
                    {
                        if (targetEntity.Removed)
                        {
                            continue;
                        }
                    }

                    for (int i = 0; i < propertyNames.Length; i++)
                    {
                        if (target == null || target.SerializableProperties == null ||
                            !target.SerializableProperties.TryGetValue(propertyNames[i], out SerializableProperty property))
                        {
                            continue;
                        }
                        ApplyToProperty(target, property, propertyEffects[i], deltaTime);
                    }
                }
            }

            if (explosion != null && entity != null)
            {
                explosion.Explode(entity.WorldPosition, damageSource: entity, attacker: user);
            }

            foreach (ISerializableEntity target in targets)
            {
                foreach (Affliction affliction in Afflictions)
                {
                    Affliction multipliedAffliction = affliction;
                    if (!disableDeltaTime)
                    {
                        multipliedAffliction = affliction.CreateMultiplied(deltaTime);
                    }

                    if (target is Character character)
                    {
                        character.LastDamageSource = entity;
                        foreach (Limb limb in character.AnimController.Limbs)
                        {
                            limb.character.DamageLimb(entity.WorldPosition, limb, new List <Affliction>()
                            {
                                multipliedAffliction
                            }, stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: affliction.Source);
                            //only apply non-limb-specific afflictions to the first limb
                            if (!affliction.Prefab.LimbSpecific)
                            {
                                break;
                            }
                        }
                    }
                    else if (target is Limb limb)
                    {
                        limb.character.DamageLimb(entity.WorldPosition, limb, new List <Affliction>()
                        {
                            multipliedAffliction
                        }, stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: affliction.Source);
                    }
                }

                foreach (Pair <string, float> reduceAffliction in ReduceAffliction)
                {
                    float     reduceAmount    = disableDeltaTime ? reduceAffliction.Second : reduceAffliction.Second * deltaTime;
                    Limb      targetLimb      = null;
                    Character targetCharacter = null;
                    if (target is Character character)
                    {
                        targetCharacter = character;
                    }
                    else if (target is Limb limb)
                    {
                        targetLimb      = limb;
                        targetCharacter = limb.character;
                    }
                    if (targetCharacter != null)
                    {
                        float prevVitality = targetCharacter.Vitality;
                        targetCharacter.CharacterHealth.ReduceAffliction(targetLimb, reduceAffliction.First, reduceAmount);
#if SERVER
                        GameMain.Server.KarmaManager.OnCharacterHealthChanged(targetCharacter, user, prevVitality - targetCharacter.Vitality);
#endif
                    }
                }
            }

            if (FireSize > 0.0f && entity != null)
            {
                var fire = new FireSource(entity.WorldPosition, hull);
                fire.Size = new Vector2(FireSize, fire.Size.Y);
            }

            bool isNotClient = GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient;
            if (isNotClient && entity != null && Entity.Spawner != null) //clients are not allowed to spawn items
            {
                foreach (ItemSpawnInfo itemSpawnInfo in spawnItems)
                {
                    switch (itemSpawnInfo.SpawnPosition)
                    {
                    case ItemSpawnInfo.SpawnPositionType.This:
                        Entity.Spawner.AddToSpawnQueue(itemSpawnInfo.ItemPrefab, entity.WorldPosition);
                        break;

                    case ItemSpawnInfo.SpawnPositionType.ThisInventory:
                    {
                        if (entity is Character character)
                        {
                            if (character.Inventory != null && character.Inventory.Items.Any(it => it == null))
                            {
                                Entity.Spawner.AddToSpawnQueue(itemSpawnInfo.ItemPrefab, character.Inventory);
                            }
                        }
                        else if (entity is Item item)
                        {
                            var inventory = item?.GetComponent <ItemContainer>()?.Inventory;
                            if (inventory != null && inventory.Items.Any(it => it == null))
                            {
                                Entity.Spawner.AddToSpawnQueue(itemSpawnInfo.ItemPrefab, inventory);
                            }
                        }
                    }
                    break;

                    case ItemSpawnInfo.SpawnPositionType.ContainedInventory:
                    {
                        Inventory thisInventory = null;
                        if (entity is Character character)
                        {
                            thisInventory = character.Inventory;
                        }
                        else if (entity is Item item)
                        {
                            thisInventory = item?.GetComponent <ItemContainer>()?.Inventory;
                        }
                        if (thisInventory != null)
                        {
                            foreach (Item item in thisInventory.Items)
                            {
                                if (item == null)
                                {
                                    continue;
                                }
                                Inventory containedInventory = item.GetComponent <ItemContainer>()?.Inventory;
                                if (containedInventory == null || !containedInventory.Items.Any(i => i == null))
                                {
                                    continue;
                                }
                                Entity.Spawner.AddToSpawnQueue(itemSpawnInfo.ItemPrefab, containedInventory);
                                break;
                            }
                        }
                    }
                    break;
                    }
                }
            }

            ApplyProjSpecific(deltaTime, entity, targets, hull);
        }
        public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            switch (type)
            {
            case ServerNetObject.ENTITY_POSITION:
                bool facingRight = AnimController.Dir > 0.0f;

                lastRecvPositionUpdateTime = (float)Lidgren.Network.NetTime.Now;

                AnimController.Frozen = false;
                Enabled = true;

                UInt16 networkUpdateID = 0;
                if (msg.ReadBoolean())
                {
                    networkUpdateID = msg.ReadUInt16();
                }
                else
                {
                    bool aimInput = msg.ReadBoolean();
                    keys[(int)InputType.Aim].Held = aimInput;
                    keys[(int)InputType.Aim].SetState(false, aimInput);

                    bool shootInput = msg.ReadBoolean();
                    keys[(int)InputType.Shoot].Held = shootInput;
                    keys[(int)InputType.Shoot].SetState(false, shootInput);

                    bool useInput = msg.ReadBoolean();
                    keys[(int)InputType.Use].Held = useInput;
                    keys[(int)InputType.Use].SetState(false, useInput);

                    if (AnimController is HumanoidAnimController)
                    {
                        bool crouching = msg.ReadBoolean();
                        keys[(int)InputType.Crouch].Held = crouching;
                        keys[(int)InputType.Crouch].SetState(false, crouching);
                    }

                    bool attackInput = msg.ReadBoolean();
                    keys[(int)InputType.Attack].Held = attackInput;
                    keys[(int)InputType.Attack].SetState(false, attackInput);

                    double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI;
                    cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f;
                    TransformCursorPos();

                    bool ragdollInput = msg.ReadBoolean();
                    keys[(int)InputType.Ragdoll].Held = ragdollInput;
                    keys[(int)InputType.Ragdoll].SetState(false, ragdollInput);

                    facingRight = msg.ReadBoolean();
                }

                bool      entitySelected    = msg.ReadBoolean();
                Character selectedCharacter = null;
                Item      selectedItem      = null;

                AnimController.Animation animation = AnimController.Animation.None;
                if (entitySelected)
                {
                    ushort characterID = msg.ReadUInt16();
                    ushort itemID      = msg.ReadUInt16();
                    selectedCharacter = FindEntityByID(characterID) as Character;
                    selectedItem      = FindEntityByID(itemID) as Item;
                    if (characterID != NullEntityID)
                    {
                        bool doingCpr = msg.ReadBoolean();
                        if (doingCpr && SelectedCharacter != null)
                        {
                            animation = AnimController.Animation.CPR;
                        }
                    }
                }

                Vector2 pos = new Vector2(
                    msg.ReadSingle(),
                    msg.ReadSingle());
                float   MaxVel         = NetConfig.MaxPhysicsBodyVelocity;
                Vector2 linearVelocity = new Vector2(
                    msg.ReadRangedSingle(-MaxVel, MaxVel, 12),
                    msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
                linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12);

                bool  fixedRotation   = msg.ReadBoolean();
                float?rotation        = null;
                float?angularVelocity = null;
                if (!fixedRotation)
                {
                    rotation = msg.ReadSingle();
                    float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
                    angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8);
                    angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8);
                }

                bool readStatus = msg.ReadBoolean();
                if (readStatus)
                {
                    ReadStatus(msg);
                    (AIController as EnemyAIController)?.PetBehavior?.ClientRead(msg);
                }

                msg.ReadPadBits();

                int index = 0;
                if (GameMain.Client.Character == this && CanMove)
                {
                    var posInfo = new CharacterStateInfo(
                        pos, rotation,
                        networkUpdateID,
                        facingRight ? Direction.Right : Direction.Left,
                        selectedCharacter, selectedItem, animation);

                    while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
                    {
                        index++;
                    }
                    memState.Insert(index, posInfo);
                }
                else
                {
                    var posInfo = new CharacterStateInfo(
                        pos, rotation,
                        linearVelocity, angularVelocity,
                        sendingTime, facingRight ? Direction.Right : Direction.Left,
                        selectedCharacter, selectedItem, animation);

                    while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp)
                    {
                        index++;
                    }
                    memState.Insert(index, posInfo);
                }

                break;

            case ServerNetObject.ENTITY_EVENT:

                int eventType = msg.ReadRangedInteger(0, 5);
                switch (eventType)
                {
                case 0:         //NetEntityEvent.Type.InventoryState
                    if (Inventory == null)
                    {
                        string errorMsg = "Received an inventory update message for an entity with no inventory (" + Name + ", removed: " + Removed + ")";
                        DebugConsole.ThrowError(errorMsg);
                        GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);

                        //read anyway to prevent messing up reading the rest of the message
                        UInt16 lastEventID = msg.ReadUInt16();
                        byte   itemCount   = msg.ReadByte();
                        for (int i = 0; i < itemCount; i++)
                        {
                            msg.ReadUInt16();
                        }
                    }
                    else
                    {
                        Inventory.ClientRead(type, msg, sendingTime);
                    }
                    break;

                case 1:         //NetEntityEvent.Type.Control
                    byte ownerID = msg.ReadByte();
                    ResetNetState();
                    if (ownerID == GameMain.Client.ID)
                    {
                        if (controlled != null)
                        {
                            LastNetworkUpdateID = controlled.LastNetworkUpdateID;
                        }

                        if (!IsDead)
                        {
                            Controlled = this;
                        }
                        IsRemotePlayer                   = false;
                        GameMain.Client.HasSpawned       = true;
                        GameMain.Client.Character        = this;
                        GameMain.LightManager.LosEnabled = true;
                    }
                    else
                    {
                        if (controlled == this)
                        {
                            Controlled     = null;
                            IsRemotePlayer = ownerID > 0;
                        }
                    }
                    break;

                case 2:         //NetEntityEvent.Type.Status
                    ReadStatus(msg);
                    break;

                case 3:         //NetEntityEvent.Type.UpdateSkills
                    int skillCount = msg.ReadByte();
                    for (int i = 0; i < skillCount; i++)
                    {
                        string skillIdentifier = msg.ReadString();
                        float  skillLevel      = msg.ReadSingle();
                        info?.SetSkillLevel(skillIdentifier, skillLevel, WorldPosition + Vector2.UnitY * 150.0f);
                    }
                    break;

                case 4:         //NetEntityEvent.Type.ExecuteAttack
                    int    attackLimbIndex = msg.ReadByte();
                    UInt16 targetEntityID  = msg.ReadUInt16();
                    int    targetLimbIndex = msg.ReadByte();

                    //255 = entity already removed, no need to do anything
                    if (attackLimbIndex == 255 || Removed)
                    {
                        break;
                    }

                    if (attackLimbIndex >= AnimController.Limbs.Length)
                    {
                        DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})");
                        break;
                    }
                    Limb attackLimb = AnimController.Limbs[attackLimbIndex];
                    Limb targetLimb = null;
                    if (!(FindEntityByID(targetEntityID) is IDamageable targetEntity))
                    {
                        DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Target entity not found (ID {targetEntityID})");
                        break;
                    }
                    if (targetEntity is Character targetCharacter)
                    {
                        if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length)
                        {
                            DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Target limb index out of bounds (target character: {targetCharacter.Name}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})");
                            break;
                        }
                        targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex];
                    }
                    if (attackLimb?.attack != null)
                    {
                        attackLimb.ExecuteAttack(targetEntity, targetLimb, out _);
                    }
                    break;

                case 5:         //NetEntityEvent.Type.AssignCampaignInteraction
                    byte campaignInteractionType = msg.ReadByte();
                    (GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType);
                    break;
                }
                msg.ReadPadBits();
                break;
            }
        }
        public override void DragCharacter(Character target, float deltaTime)
        {
            if (target == null)
            {
                return;
            }

            Limb mouthLimb = Array.Find(Limbs, l => l != null && l.MouthPos.HasValue);

            if (mouthLimb == null)
            {
                mouthLimb = GetLimb(LimbType.Head);
            }

            if (mouthLimb == null)
            {
                DebugConsole.ThrowError("Character \"" + character.SpeciesName + "\" failed to eat a target (a head or a limb with a mouthpos required)");
                return;
            }

            Character targetCharacter = target;
            float     eatSpeed        = character.Mass / targetCharacter.Mass * 0.1f;

            eatTimer += deltaTime * eatSpeed;

            Vector2 mouthPos          = GetMouthPosition().Value;
            Vector2 attackSimPosition = character.Submarine == null?ConvertUnits.ToSimUnits(target.WorldPosition) : target.SimPosition;

            Vector2 limbDiff = attackSimPosition - mouthPos;
            float   limbDist = limbDiff.Length();

            if (limbDist < 1.0f)
            {
                //pull the target character to the position of the mouth
                //(+ make the force fluctuate to waggle the character a bit)
                if (CanDrag(target))
                {
                    targetCharacter.AnimController.MainLimb.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f));
                    targetCharacter.AnimController.MainLimb.body.SmoothRotate(mouthLimb.Rotation, 20.0f);
                    targetCharacter.AnimController.Collider.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f));
                }

                //pull the character's mouth to the target character (again with a fluctuating force)
                float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f));
                mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength);

                character.ApplyStatusEffects(ActionType.OnEating, deltaTime);

                if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f)
                {
                    //apply damage to the target character to get some blood particles flying
                    targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, 0.0f, 20.0f, 0.0f, false);

                    //keep severing joints until there is only one limb left
                    LimbJoint[] nonSeveredJoints = Array.FindAll(targetCharacter.AnimController.LimbJoints,
                                                                 l => !l.IsSevered && l.CanBeSevered && l.LimbA != null && !l.LimbA.IsSevered && l.LimbB != null && !l.LimbB.IsSevered);
                    if (nonSeveredJoints.Length == 0)
                    {
                        //only one limb left, the character is now full eaten
                        Entity.Spawner.AddToRemoveQueue(targetCharacter);
                        character.SelectedCharacter = null;
                    }
                    else //sever a random joint
                    {
                        targetCharacter.AnimController.SeverLimbJoint(nonSeveredJoints[Rand.Int(nonSeveredJoints.Length)]);
                    }
                }
            }
            else
            {
                character.SelectedCharacter = null;
            }
        }
Example #6
0
        private void UpdateAttack(float deltaTime)
        {
            if (selectedAiTarget == null || selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed)
            {
                state = AiState.None;
                return;
            }

            selectedTargetMemory.Priority -= deltaTime;

            Vector2 attackSimPosition = Character.Submarine == null?ConvertUnits.ToSimUnits(selectedAiTarget.WorldPosition) : selectedAiTarget.SimPosition;

            if (wallAttackPos != Vector2.Zero && targetEntity != null)
            {
                attackSimPosition = wallAttackPos;

                if (selectedAiTarget.Entity != null && Character.Submarine == null && selectedAiTarget.Entity.Submarine != null)
                {
                    attackSimPosition += ConvertUnits.ToSimUnits(selectedAiTarget.Entity.Submarine.Position);
                }
            }

            if (Math.Abs(Character.AnimController.movement.X) > 0.1f && !Character.AnimController.InWater)
            {
                Character.AnimController.TargetDir = Character.SimPosition.X < attackSimPosition.X ? Direction.Right : Direction.Left;
            }

            if (coolDownTimer > 0.0f)
            {
                UpdateCoolDown(attackSimPosition, deltaTime);
                return;
            }

            if (raycastTimer > 0.0)
            {
                raycastTimer -= deltaTime;
            }
            else
            {
                GetTargetEntity();

                raycastTimer = RaycastInterval;
            }

            //steeringManager.SteeringAvoid(deltaTime, 1.0f);

            Limb attackLimb = attackingLimb;

            //check if any of the limbs is close enough to attack the target
            if (attackingLimb == null)
            {
                foreach (Limb limb in Character.AnimController.Limbs)
                {
                    if (limb.attack == null)
                    {
                        continue;
                    }
                    attackLimb = limb;

                    if (ConvertUnits.ToDisplayUnits(Vector2.Distance(limb.SimPosition, attackSimPosition)) > limb.attack.Range)
                    {
                        continue;
                    }

                    attackingLimb = limb;
                    break;
                }

                if (Character.IsRemotePlayer)
                {
                    if (!Character.IsKeyDown(InputType.Attack))
                    {
                        return;
                    }
                }
            }
            if (attackLimb != null)
            {
                steeringManager.SteeringSeek(attackSimPosition - (attackLimb.SimPosition - SimPosition));

                if (steeringManager is IndoorsSteeringManager)
                {
                    var indoorsSteering = (IndoorsSteeringManager)steeringManager;
                    if (indoorsSteering.CurrentPath != null && (indoorsSteering.CurrentPath.Finished || indoorsSteering.CurrentPath.Unreachable))
                    {
                        steeringManager.SteeringManual(deltaTime, attackSimPosition - attackLimb.SimPosition);
                    }
                }

                if (attackingLimb != null)
                {
                    UpdateLimbAttack(deltaTime, attackingLimb, attackSimPosition);
                }
            }
        }
        private void HandleLimbCollision(Impact collision, Limb limb)
        {
            if (limb?.body?.FarseerBody == null || limb.character == null)
            {
                return;
            }

            if (limb.Mass > 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(limb.Mass / 100.0f, 1);

                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;

                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);
                    }
                }
            }
        }
Example #8
0
        public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
        {
            string errorMsg = "";

            if (extraData == null || extraData.Length == 0 || !(extraData[0] is NetEntityEvent.Type))
            {
                if (extraData == null)
                {
                    errorMsg = "Failed to write a network event for the item \"" + Name + "\" - event data was null.";
                }
                else if (extraData.Length == 0)
                {
                    errorMsg = "Failed to write a network event for the item \"" + Name + "\" - event data was empty.";
                }
                else
                {
                    errorMsg = "Failed to write a network event for the item \"" + Name + "\" - event type not set.";
                }
                msg.WriteRangedInteger((int)NetEntityEvent.Type.Invalid, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);
                DebugConsole.Log(errorMsg);
                GameAnalyticsManager.AddErrorEventOnce("Item.ServerWrite:InvalidData" + Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                return;
            }

            int initialWritePos = msg.LengthBits;

            NetEntityEvent.Type eventType = (NetEntityEvent.Type)extraData[0];
            msg.WriteRangedInteger((int)eventType, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);
            switch (eventType)
            {
            case NetEntityEvent.Type.ComponentState:
                if (extraData.Length < 2 || !(extraData[1] is int))
                {
                    errorMsg = "Failed to write a component state event for the item \"" + Name + "\" - component index not given.";
                    break;
                }
                int componentIndex = (int)extraData[1];
                if (componentIndex < 0 || componentIndex >= components.Count)
                {
                    errorMsg = "Failed to write a component state event for the item \"" + Name + "\" - component index out of range (" + componentIndex + ").";
                    break;
                }
                else if (!(components[componentIndex] is IServerSerializable))
                {
                    errorMsg = "Failed to write a component state event for the item \"" + Name + "\" - component \"" + components[componentIndex] + "\" is not server serializable.";
                    break;
                }
                msg.WriteRangedInteger(componentIndex, 0, components.Count - 1);
                (components[componentIndex] as IServerSerializable).ServerWrite(msg, c, extraData);
                break;

            case NetEntityEvent.Type.InventoryState:
                if (extraData.Length < 2 || !(extraData[1] is int))
                {
                    errorMsg = "Failed to write an inventory state event for the item \"" + Name + "\" - component index not given.";
                    break;
                }
                int containerIndex = (int)extraData[1];
                if (containerIndex < 0 || containerIndex >= components.Count)
                {
                    errorMsg = "Failed to write an inventory state event for the item \"" + Name + "\" - container index out of range (" + containerIndex + ").";
                    break;
                }
                else if (!(components[containerIndex] is ItemContainer))
                {
                    errorMsg = "Failed to write an inventory state event for the item \"" + Name + "\" - component \"" + components[containerIndex] + "\" is not server serializable.";
                    break;
                }
                msg.WriteRangedInteger(containerIndex, 0, components.Count - 1);
                msg.Write(GameMain.Server.EntityEventManager.Events.Last()?.ID ?? (ushort)0);
                (components[containerIndex] as ItemContainer).Inventory.ServerWrite(msg, c);
                break;

            case NetEntityEvent.Type.Status:
                msg.Write(condition);
                break;

            case NetEntityEvent.Type.Treatment:
            {
                ItemComponent targetComponent = (ItemComponent)extraData[1];
                ActionType    actionType      = (ActionType)extraData[2];
                ushort        targetID        = (ushort)extraData[3];
                Limb          targetLimb      = (Limb)extraData[4];

                Character targetCharacter = FindEntityByID(targetID) as Character;
                byte      targetLimbIndex = targetLimb != null && targetCharacter != null ? (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb) : (byte)255;

                msg.Write((byte)components.IndexOf(targetComponent));
                msg.WriteRangedInteger((int)actionType, 0, Enum.GetValues(typeof(ActionType)).Length - 1);
                msg.Write(targetID);
                msg.Write(targetLimbIndex);
            }
            break;

            case NetEntityEvent.Type.ApplyStatusEffect:
            {
                ActionType    actionType      = (ActionType)extraData[1];
                ItemComponent targetComponent = extraData.Length > 2 ? (ItemComponent)extraData[2] : null;
                ushort        characterID     = extraData.Length > 3 ? (ushort)extraData[3] : (ushort)0;
                Limb          targetLimb      = extraData.Length > 4 ? (Limb)extraData[4] : null;
                ushort        useTargetID     = extraData.Length > 5 ? (ushort)extraData[5] : (ushort)0;
                Vector2?      worldPosition   = null;
                if (extraData.Length > 6)
                {
                    worldPosition = (Vector2)extraData[6];
                }

                Character targetCharacter = FindEntityByID(characterID) as Character;
                byte      targetLimbIndex = targetLimb != null && targetCharacter != null ? (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb) : (byte)255;

                msg.WriteRangedInteger((int)actionType, 0, Enum.GetValues(typeof(ActionType)).Length - 1);
                msg.Write((byte)(targetComponent == null ? 255 : components.IndexOf(targetComponent)));
                msg.Write(characterID);
                msg.Write(targetLimbIndex);
                msg.Write(useTargetID);
                msg.Write(worldPosition.HasValue);
                if (worldPosition.HasValue)
                {
                    msg.Write(worldPosition.Value.X);
                    msg.Write(worldPosition.Value.Y);
                }
            }
            break;

            case NetEntityEvent.Type.ChangeProperty:
                try
                {
                    WritePropertyChange(msg, extraData, inGameEditableOnly: !GameMain.NetworkMember.IsServer);
                }
                catch (Exception e)
                {
                    errorMsg = "Failed to write a ChangeProperty network event for the item \"" + Name + "\" (" + e.Message + ")";
                }
                break;

            case NetEntityEvent.Type.Upgrade:
                if (extraData.Length > 0 && extraData[1] is Upgrade upgrade)
                {
                    var upgradeTargets = upgrade.TargetComponents;
                    msg.Write(upgrade.Identifier);
                    msg.Write((byte)upgrade.Level);
                    msg.Write((byte)upgradeTargets.Count);
                    foreach (var(_, value) in upgrade.TargetComponents)
                    {
                        msg.Write((byte)value.Length);
                        foreach (var propertyReference in value)
                        {
                            object originalValue = propertyReference.OriginalValue;
                            msg.Write((float)(originalValue ?? -1));
                        }
                    }
                }
                else
                {
                    errorMsg = extraData.Length > 0
                            ? $"Failed to write a network event for the item \"{Name}\" - \"{extraData[1].GetType()}\" is not a valid upgrade."
                            : $"Failed to write a network event for the item \"{Name}\". No upgrade specified.";
                }
                break;

            default:
                errorMsg = "Failed to write a network event for the item \"" + Name + "\" - \"" + eventType + "\" is not a valid entity event type for items.";
                break;
            }

            if (!string.IsNullOrEmpty(errorMsg))
            {
                //something went wrong - rewind the write position and write invalid event type to prevent creating an unreadable event
                msg.BitPosition = initialWritePos;
                msg.LengthBits  = initialWritePos;
                msg.WriteRangedInteger((int)NetEntityEvent.Type.Invalid, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);
                DebugConsole.Log(errorMsg);
                GameAnalyticsManager.AddErrorEventOnce("Item.ServerWrite:" + errorMsg, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
            }
        }
Example #9
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            NetEntityEvent.Type eventType =
                (NetEntityEvent.Type)msg.ReadRangedInteger(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);

            c.KickAFKTimer = 0.0f;

            switch (eventType)
            {
            case NetEntityEvent.Type.ComponentState:
                int componentIndex = msg.ReadRangedInteger(0, components.Count - 1);
                (components[componentIndex] as IClientSerializable).ServerRead(type, msg, c);
                break;

            case NetEntityEvent.Type.InventoryState:
                int containerIndex = msg.ReadRangedInteger(0, components.Count - 1);
                (components[containerIndex] as ItemContainer).Inventory.ServerRead(type, msg, c);
                break;

            case NetEntityEvent.Type.Treatment:
                if (c.Character == null || !c.Character.CanInteractWith(this))
                {
                    return;
                }

                UInt16 characterID = msg.ReadUInt16();
                byte   limbIndex   = msg.ReadByte();

                Character targetCharacter = FindEntityByID(characterID) as Character;
                if (targetCharacter == null)
                {
                    break;
                }
                if (targetCharacter != c.Character && c.Character.SelectedCharacter != targetCharacter)
                {
                    break;
                }

                Limb targetLimb = limbIndex < targetCharacter.AnimController.Limbs.Length ? targetCharacter.AnimController.Limbs[limbIndex] : null;

                if (ContainedItems == null || ContainedItems.All(i => i == null))
                {
                    GameServer.Log(GameServer.CharacterLogName(c.Character) + " used item " + Name, ServerLog.MessageType.ItemInteraction);
                }
                else
                {
                    GameServer.Log(
                        GameServer.CharacterLogName(c.Character) + " used item " + Name + " (contained items: " + string.Join(", ", ContainedItems.Select(i => i.Name)) + ")",
                        ServerLog.MessageType.ItemInteraction);
                }

                ApplyTreatment(c.Character, targetCharacter, targetLimb);

                break;

            case NetEntityEvent.Type.ChangeProperty:
                ReadPropertyChange(msg, inGameEditableOnly: GameMain.NetworkMember.IsServer, sender: c);
                break;

            case NetEntityEvent.Type.Combine:
                UInt16 combineTargetID = msg.ReadUInt16();
                Item   combineTarget   = FindEntityByID(combineTargetID) as Item;
                if (combineTarget == null || !c.Character.CanInteractWith(this) || !c.Character.CanInteractWith(combineTarget))
                {
                    return;
                }
                Combine(combineTarget, c.Character);
                break;
            }
        }
Example #10
0
        public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
        {
            string errorMsg = "";

            if (extraData == null || extraData.Length == 0 || !(extraData[0] is NetEntityEvent.Type))
            {
                if (extraData == null)
                {
                    errorMsg = "Failed to write a network event for the item \"" + Name + "\" - event data was null.";
                }
                else if (extraData.Length == 0)
                {
                    errorMsg = "Failed to write a network event for the item \"" + Name + "\" - event data was empty.";
                }
                else
                {
                    errorMsg = "Failed to write a network event for the item \"" + Name + "\" - event type not set.";
                }
                msg.WriteRangedInteger(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1, (int)NetEntityEvent.Type.Invalid);
                DebugConsole.Log(errorMsg);
                GameAnalyticsManager.AddErrorEventOnce("Item.ServerWrite:InvalidData" + Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                return;
            }

            int initialWritePos = msg.LengthBits;

            NetEntityEvent.Type eventType = (NetEntityEvent.Type)extraData[0];
            msg.WriteRangedInteger(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1, (int)eventType);
            switch (eventType)
            {
            case NetEntityEvent.Type.ComponentState:
                if (extraData.Length < 2 || !(extraData[1] is int))
                {
                    errorMsg = "Failed to write a component state event for the item \"" + Name + "\" - component index not given.";
                    break;
                }
                int componentIndex = (int)extraData[1];
                if (componentIndex < 0 || componentIndex >= components.Count)
                {
                    errorMsg = "Failed to write a component state event for the item \"" + Name + "\" - component index out of range (" + componentIndex + ").";
                    break;
                }
                else if (!(components[componentIndex] is IServerSerializable))
                {
                    errorMsg = "Failed to write a component state event for the item \"" + Name + "\" - component \"" + components[componentIndex] + "\" is not server serializable.";
                    break;
                }
                msg.WriteRangedInteger(0, components.Count - 1, componentIndex);
                (components[componentIndex] as IServerSerializable).ServerWrite(msg, c, extraData);
                break;

            case NetEntityEvent.Type.InventoryState:
                if (extraData.Length < 2 || !(extraData[1] is int))
                {
                    errorMsg = "Failed to write an inventory state event for the item \"" + Name + "\" - component index not given.";
                    break;
                }
                int containerIndex = (int)extraData[1];
                if (containerIndex < 0 || containerIndex >= components.Count)
                {
                    errorMsg = "Failed to write an inventory state event for the item \"" + Name + "\" - container index out of range (" + containerIndex + ").";
                    break;
                }
                else if (!(components[containerIndex] is ItemContainer))
                {
                    errorMsg = "Failed to write an inventory state event for the item \"" + Name + "\" - component \"" + components[containerIndex] + "\" is not server serializable.";
                    break;
                }
                msg.WriteRangedInteger(0, components.Count - 1, containerIndex);
                (components[containerIndex] as ItemContainer).Inventory.ServerWrite(msg, c);
                break;

            case NetEntityEvent.Type.Status:
                msg.Write(condition);
                break;

            case NetEntityEvent.Type.Treatment:
            {
                ItemComponent targetComponent = (ItemComponent)extraData[1];
                ActionType    actionType      = (ActionType)extraData[2];
                ushort        targetID        = (ushort)extraData[3];
                Limb          targetLimb      = (Limb)extraData[4];

                Character targetCharacter = FindEntityByID(targetID) as Character;
                byte      targetLimbIndex = targetLimb != null && targetCharacter != null ? (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb) : (byte)255;

                msg.Write((byte)components.IndexOf(targetComponent));
                msg.WriteRangedInteger(0, Enum.GetValues(typeof(ActionType)).Length - 1, (int)actionType);
                msg.Write(targetID);
                msg.Write(targetLimbIndex);
            }
            break;

            case NetEntityEvent.Type.ApplyStatusEffect:
            {
                ActionType    actionType      = (ActionType)extraData[1];
                ItemComponent targetComponent = extraData.Length > 2 ? (ItemComponent)extraData[2] : null;
                ushort        targetID        = extraData.Length > 3 ? (ushort)extraData[3] : (ushort)0;
                Limb          targetLimb      = extraData.Length > 4 ? (Limb)extraData[4] : null;

                Character targetCharacter = FindEntityByID(targetID) as Character;
                byte      targetLimbIndex = targetLimb != null && targetCharacter != null ? (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb) : (byte)255;

                msg.WriteRangedInteger(0, Enum.GetValues(typeof(ActionType)).Length - 1, (int)actionType);
                msg.Write((byte)(targetComponent == null ? 255 : components.IndexOf(targetComponent)));
                msg.Write(targetID);
                msg.Write(targetLimbIndex);
            }
            break;

            case NetEntityEvent.Type.ChangeProperty:
                try
                {
                    WritePropertyChange(msg, extraData, false);
                }
                catch (Exception e)
                {
                    errorMsg = "Failed to write a ChangeProperty network event for the item \"" + Name + "\" (" + e.Message + ")";
                }
                break;

            default:
                errorMsg = "Failed to write a network event for the item \"" + Name + "\" - \"" + eventType + "\" is not a valid entity event type for items.";
                break;
            }

            if (!string.IsNullOrEmpty(errorMsg))
            {
                //something went wrong - rewind the write position and write invalid event type to prevent creating an unreadable event
                msg.ReadBits(msg.Data, 0, initialWritePos);
                msg.LengthBits = initialWritePos;
                msg.WriteRangedInteger(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1, (int)NetEntityEvent.Type.Invalid);
                DebugConsole.Log(errorMsg);
                GameAnalyticsManager.AddErrorEventOnce("Item.ServerWrite:" + errorMsg, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
            }
        }
        private void HandleLimbCollision(Contact contact, Limb limb)
        {
            if (limb.Mass > 100.0f)
            {
                Vector2 normal = 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)
            {
                Vector2 contactNormal;
                FixedArray2 <Vector2> temp;
                levelContact.GetWorldManifold(out contactNormal, out temp);

                //if the contact normal is pointing from the limb towards the level cell it's touching, flip the normal
                VoronoiCell cell = levelContact.FixtureB.Body.UserData is VoronoiCell ?
                                   ((VoronoiCell)levelContact.FixtureB.Body.UserData) : ((VoronoiCell)levelContact.FixtureA.Body.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.0f)
            {
                Body.LinearVelocity -= Vector2.Normalize(Body.LinearVelocity) * contactDot;

                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);
                    }
                }
            }
        }
Example #12
0
        public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
        {
            if (!Visible)
            {
                return;
            }

            Color color = (IsSelected && editing) ? Color.Red : GetSpriteColor();

            if (isHighlighted)
            {
                color = Color.Orange;
            }

            Sprite           activeSprite            = prefab.sprite;
            BrokenItemSprite fadeInBrokenSprite      = null;
            float            fadeInBrokenSpriteAlpha = 0.0f;

            if (condition < 100.0f)
            {
                for (int i = 0; i < prefab.BrokenSprites.Count; i++)
                {
                    if (condition <= prefab.BrokenSprites[i].MaxCondition)
                    {
                        activeSprite = prefab.BrokenSprites[i].Sprite;
                        break;
                    }

                    if (prefab.BrokenSprites[i].FadeIn)
                    {
                        float min = i > 0 ? prefab.BrokenSprites[i].MaxCondition : 0.0f;
                        float max = i < prefab.BrokenSprites.Count - 1 ? prefab.BrokenSprites[i + 1].MaxCondition : 100.0f;
                        fadeInBrokenSpriteAlpha = 1.0f - ((condition - min) / (max - min));
                        if (fadeInBrokenSpriteAlpha > 0.0f && fadeInBrokenSpriteAlpha < 1.0f)
                        {
                            fadeInBrokenSprite = prefab.BrokenSprites[i];
                        }
                    }
                }
            }

            Sprite selectedSprite = prefab.GetActiveSprite(condition);

            if (selectedSprite != null)
            {
                SpriteEffects oldEffects = selectedSprite.effects;
                selectedSprite.effects ^= SpriteEffects;

                float depth = GetDrawDepth();

                if (body == null)
                {
                    if (prefab.ResizeHorizontal || prefab.ResizeVertical || SpriteEffects.HasFlag(SpriteEffects.FlipHorizontally) || SpriteEffects.HasFlag(SpriteEffects.FlipVertically))
                    {
                        selectedSprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)), new Vector2(rect.Width, rect.Height), color);
                        fadeInBrokenSprite?.Sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)), new Vector2(rect.Width, rect.Height), color * fadeInBrokenSpriteAlpha, Point.Zero, selectedSprite.Depth - 0.000001f);
                    }
                    else
                    {
                        selectedSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y), color, 0.0f, 1.0f, SpriteEffects.None, depth);
                        fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y), color * fadeInBrokenSpriteAlpha, 0.0f, 1.0f, SpriteEffects.None, depth - 0.000001f);
                    }
                }
                else if (body.Enabled)
                {
                    var holdable = GetComponent <Holdable>();
                    if (holdable != null && holdable.Picker?.AnimController != null)
                    {
                        if (holdable.Picker.SelectedItems[0] == this)
                        {
                            Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.RightHand);
                            depth = holdLimb.sprite.Depth + 0.000001f;
                            foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
                            {
                                if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null)
                                {
                                    depth = Math.Min(wearableSprite.Sprite.Depth, depth);
                                }
                            }
                        }
                        else if (holdable.Picker.SelectedItems[1] == this)
                        {
                            Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.LeftHand);
                            depth = holdLimb.sprite.Depth - 0.000001f;
                            foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
                            {
                                if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null)
                                {
                                    depth = Math.Max(wearableSprite.Sprite.Depth, depth);
                                }
                            }
                        }
                    }
                    body.Draw(spriteBatch, selectedSprite, color, depth);
                    if (fadeInBrokenSprite != null)
                    {
                        body.Draw(spriteBatch, fadeInBrokenSprite.Sprite, color * fadeInBrokenSpriteAlpha, depth - 0.000001f);
                    }
                }

                selectedSprite.effects = oldEffects;
            }


            List <IDrawableComponent> staticDrawableComponents = new List <IDrawableComponent>(drawableComponents); //static list to compensate for drawable toggling

            for (int i = 0; i < staticDrawableComponents.Count; i++)
            {
                staticDrawableComponents[i].Draw(spriteBatch, editing);
            }

            if (GameMain.DebugDraw && aiTarget != null)
            {
                aiTarget.Draw(spriteBatch);
            }

            if (!editing || (body != null && !body.Enabled))
            {
                return;
            }

            if (IsSelected || isHighlighted)
            {
                GUI.DrawRectangle(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)), new Vector2(rect.Width, rect.Height), Color.Green, false, 0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f));

                foreach (Rectangle t in prefab.Triggers)
                {
                    Rectangle transformedTrigger = TransformTrigger(t);

                    Vector2 rectWorldPos = new Vector2(transformedTrigger.X, transformedTrigger.Y);
                    if (Submarine != null)
                    {
                        rectWorldPos += Submarine.Position;
                    }
                    rectWorldPos.Y = -rectWorldPos.Y;

                    GUI.DrawRectangle(spriteBatch,
                                      rectWorldPos,
                                      new Vector2(transformedTrigger.Width, transformedTrigger.Height),
                                      Color.Green,
                                      false,
                                      0,
                                      (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f));
                }
            }

            if (!ShowLinks)
            {
                return;
            }

            foreach (MapEntity e in linkedTo)
            {
                GUI.DrawLine(spriteBatch,
                             new Vector2(WorldPosition.X, -WorldPosition.Y),
                             new Vector2(e.WorldPosition.X, -e.WorldPosition.Y),
                             Color.Red * 0.3f);
            }
        }
Example #13
0
        public override void UpdateAnim(float deltaTime)
        {
            if (Frozen)
            {
                return;
            }

            if (character.IsDead || character.IsUnconscious || character.Stun > 0.0f)
            {
                Collider.FarseerBody.FixedRotation = false;

                if (character.IsRemotePlayer)
                {
                    if (!SimplePhysicsEnabled)
                    {
                        MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
                        MainLimb.PullJointEnabled      = true;
                    }
                }
                else
                {
                    Vector2 diff = (MainLimb.SimPosition - Collider.SimPosition);
                    if (diff.LengthSquared() > 10.0f * 10.0f)
                    {
                        Collider.SetTransform(MainLimb.SimPosition, MainLimb.Rotation);
                    }
                    else
                    {
                        Collider.LinearVelocity = diff * 60.0f;
                        Collider.SmoothRotate(MainLimb.Rotation);
                    }
                }

                if (character.IsDead && deathAnimTimer < deathAnimDuration)
                {
                    deathAnimTimer += deltaTime;
                    UpdateDying(deltaTime);
                }

                return;
            }

            //re-enable collider
            if (!Collider.Enabled)
            {
                var lowestLimb = FindLowestLimb();

                Collider.SetTransform(new Vector2(
                                          Collider.SimPosition.X,
                                          Math.Max(lowestLimb.SimPosition.Y + (Collider.radius + Collider.height / 2), Collider.SimPosition.Y)),
                                      0.0f);

                Collider.Enabled = true;
            }

            ResetPullJoints();

            if (strongestImpact > 0.0f)
            {
                character.Stun  = MathHelper.Clamp(strongestImpact * 0.5f, character.Stun, 5.0f);
                strongestImpact = 0.0f;
            }


            if (inWater)
            {
                Collider.FarseerBody.FixedRotation = false;
                UpdateSineAnim(deltaTime);
            }
            else if (currentHull != null && CanEnterSubmarine)
            {
                if (Math.Abs(MathUtils.GetShortestAngle(Collider.Rotation, 0.0f)) > 0.001f)
                {
                    //rotate collider back upright
                    Collider.AngularVelocity           = MathUtils.GetShortestAngle(Collider.Rotation, 0.0f) * 60.0f;
                    Collider.FarseerBody.FixedRotation = false;
                }
                else
                {
                    Collider.FarseerBody.FixedRotation = true;
                }

                UpdateWalkAnim(deltaTime);
            }

            //don't flip or drag when simply physics is enabled
            if (SimplePhysicsEnabled)
            {
                return;
            }

            if (!character.IsRemotePlayer)
            {
                if (mirror || !inWater)
                {
                    if (targetMovement.X > 0.1f && targetMovement.X > Math.Abs(targetMovement.Y) * 0.5f)
                    {
                        TargetDir = Direction.Right;
                    }
                    else if (targetMovement.X < -0.1f && targetMovement.X < -Math.Abs(targetMovement.Y) * 0.5f)
                    {
                        TargetDir = Direction.Left;
                    }
                }
                else
                {
                    Limb head = GetLimb(LimbType.Head);
                    if (head == null)
                    {
                        head = GetLimb(LimbType.Torso);
                    }

                    float rotation = MathUtils.WrapAngleTwoPi(head.Rotation);
                    rotation = MathHelper.ToDegrees(rotation);

                    if (rotation < 0.0f)
                    {
                        rotation += 360;
                    }

                    if (rotation > 20 && rotation < 160)
                    {
                        TargetDir = Direction.Left;
                    }
                    else if (rotation > 200 && rotation < 340)
                    {
                        TargetDir = Direction.Right;
                    }
                }
            }

            if (character.SelectedCharacter != null)
            {
                DragCharacter(character.SelectedCharacter);
            }

            if (!flip)
            {
                return;
            }

            flipTimer += deltaTime;

            if (TargetDir != Direction.None && TargetDir != dir)
            {
                if (flipTimer > 1.0f || character.IsRemotePlayer)
                {
                    Flip();
                    if (mirror || !inWater)
                    {
                        Mirror();
                    }
                    flipTimer = 0.0f;
                }
            }
        }
Example #14
0
        private void UpdateEating(float deltaTime)
        {
            if (selectedAiTarget == null || selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed)
            {
                state = AIState.None;
                return;
            }

            Limb mouthLimb = Array.Find(Character.AnimController.Limbs, l => l != null && l.MouthPos.HasValue);

            if (mouthLimb == null)
            {
                mouthLimb = Character.AnimController.GetLimb(LimbType.Head);
            }

            if (mouthLimb == null)
            {
                DebugConsole.ThrowError("Character \"" + Character.SpeciesName + "\" failed to eat a target (a head or a limb with a mouthpos required)");
                state = AIState.None;
                return;
            }

            Character targetCharacter = selectedAiTarget.Entity as Character;
            float     eatSpeed        = Character.Mass / targetCharacter.Mass * 0.1f;

            eatTimer += deltaTime * eatSpeed;

            Vector2 mouthPos = mouthLimb.SimPosition;

            if (mouthLimb.MouthPos.HasValue)
            {
                float cos = (float)Math.Cos(mouthLimb.Rotation);
                float sin = (float)Math.Sin(mouthLimb.Rotation);

                mouthPos += new Vector2(
                    mouthLimb.MouthPos.Value.X * cos - mouthLimb.MouthPos.Value.Y * sin,
                    mouthLimb.MouthPos.Value.X * sin + mouthLimb.MouthPos.Value.Y * cos);
            }

            Vector2 attackSimPosition = Character.Submarine == null?ConvertUnits.ToSimUnits(selectedAiTarget.WorldPosition) : selectedAiTarget.SimPosition;

            Vector2 limbDiff = attackSimPosition - mouthPos;
            float   limbDist = limbDiff.Length();

            if (limbDist < 1.0f)
            {
                //pull the target character to the position of the mouth
                //(+ make the force fluctuate to waggle the character a bit)
                targetCharacter.AnimController.MainLimb.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f));
                targetCharacter.AnimController.MainLimb.body.SmoothRotate(mouthLimb.Rotation);
                targetCharacter.AnimController.Collider.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f));

                //pull the character's mouth to the target character (again with a fluctuating force)
                float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f));
                steeringManager.SteeringManual(deltaTime, limbDiff * pullStrength);
                mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength);

                //GameMain.NilMod.CreatureHealthGainEatingPercent;

                if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f)
                {
                    //Kill living players that control the character if being actively consumed.
                    //Before this point is reached a fish could be prevented with stun from severing limbs and thus still save a player.
                    if (!targetCharacter.IsDead && targetCharacter.IsRemotePlayer)
                    {
                        targetCharacter.Kill(CauseOfDeath.Damage, true);
                    }

                    //apply damage to the target character to get some blood particles flying
                    targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), Rand.Range(15.0f, 40.0f), false);

                    //Lets make this extra bloody, perhaps a client will sync it.
                    targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), Rand.Range(15.0f, 40.0f), false);
                    targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), Rand.Range(15.0f, 40.0f), false);
                    targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), Rand.Range(15.0f, 40.0f), false);

                    //keep severing joints until there is only one limb left
                    LimbJoint[] nonSeveredJoints = Array.FindAll(targetCharacter.AnimController.LimbJoints, l => !l.IsSevered && l.CanBeSevered);
                    if (nonSeveredJoints.Length == 0)
                    {
                        //only one limb left, the character is now full eaten

                        if (GameMain.Server != null)
                        {
                            GameMain.Server.ServerLog.WriteLine(Character.Name + " has finished eating " + targetCharacter.Name, Networking.ServerLog.MessageType.Spawns);
                        }

                        Entity.Spawner.AddToRemoveQueue(targetCharacter);

                        selectedAiTarget = null;
                        state            = AIState.None;
                    }
                    else //sever a random joint
                    {
                        targetCharacter.AnimController.SeverLimbJoint(nonSeveredJoints[Rand.Int(nonSeveredJoints.Length)]);
                        if (GameMain.Server != null)
                        {
                            GameMain.Server.CreateEntityEvent(targetCharacter, new object[] { Barotrauma.Networking.NetEntityEvent.Type.Status });
                        }
                    }
                }
            }
            else if (limbDist < 2.0f)
            {
                steeringManager.SteeringManual(deltaTime, limbDiff);
                Character.AnimController.Collider.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f, mouthPos);
            }
            else
            {
                steeringManager.SteeringSeek(attackSimPosition - (mouthPos - SimPosition), 3);
            }
        }
Example #15
0
        /// <summary>
        /// Returns true if the attack successfully hit something. If the distance is not given, it will be calculated.
        /// </summary>
        public bool UpdateAttack(float deltaTime, Vector2 attackSimPos, IDamageable damageTarget, out AttackResult attackResult, float distance = -1, Limb targetLimb = null)
        {
            attackResult = default(AttackResult);
            float dist       = distance > -1 ? distance : ConvertUnits.ToDisplayUnits(Vector2.Distance(SimPosition, attackSimPos));
            bool  wasRunning = attack.IsRunning;

            attack.UpdateAttackTimer(deltaTime);

            bool wasHit        = false;
            Body structureBody = null;

            if (damageTarget != null)
            {
                switch (attack.HitDetectionType)
                {
                case HitDetection.Distance:
                    if (dist < attack.DamageRange)
                    {
                        if (ignoredBodies == null)
                        {
                            ignoredBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody).ToList();
                            ignoredBodies.Add(character.AnimController.Collider.FarseerBody);
                        }

                        structureBody = Submarine.PickBody(
                            SimPosition, attackSimPos,
                            ignoredBodies, Physics.CollisionWall);

                        if (damageTarget is Item)
                        {
                            // If the attack is aimed to an item and hits an item, it's successful.
                            // Ignore blocking on items, because it causes cases where a Mudraptor cannot hit the hatch, for example.
                            wasHit = true;
                        }
                        else if (damageTarget is Structure wall && structureBody != null &&
                                 (structureBody.UserData is Structure || (structureBody.UserData is Submarine sub && sub == wall.Submarine)))
                        {
                            // If the attack is aimed to a structure (wall) and hits a structure or the sub, it's successful
                            wasHit = true;
                        }
                        else
                        {
                            // If there is nothing between, the hit is successful
                            wasHit = structureBody == null;
                        }
                    }
                    break;
Example #16
0
        private void UpdateEating(float deltaTime)
        {
            if (selectedAiTarget == null || selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed)
            {
                state = AIState.None;
                return;
            }

            Limb mouthLimb = Array.Find(Character.AnimController.Limbs, l => l != null && l.MouthPos.HasValue);

            if (mouthLimb == null)
            {
                mouthLimb = Character.AnimController.GetLimb(LimbType.Head);
            }

            if (mouthLimb == null)
            {
                DebugConsole.ThrowError("Character \"" + Character.SpeciesName + "\" failed to eat a target (a head or a limb with a mouthpos required)");
                state = AIState.None;
                return;
            }

            Character targetCharacter = selectedAiTarget.Entity as Character;
            float     eatSpeed        = Character.Mass / targetCharacter.Mass * 0.1f;

            eatTimer += deltaTime * eatSpeed;

            Vector2 mouthPos = mouthLimb.SimPosition;

            if (mouthLimb.MouthPos.HasValue)
            {
                float cos = (float)Math.Cos(mouthLimb.Rotation);
                float sin = (float)Math.Sin(mouthLimb.Rotation);

                mouthPos += new Vector2(
                    mouthLimb.MouthPos.Value.X * cos - mouthLimb.MouthPos.Value.Y * sin,
                    mouthLimb.MouthPos.Value.X * sin + mouthLimb.MouthPos.Value.Y * cos);
            }

            Vector2 attackSimPosition = Character.Submarine == null?ConvertUnits.ToSimUnits(selectedAiTarget.WorldPosition) : selectedAiTarget.SimPosition;


            Vector2 limbDiff = attackSimPosition - mouthPos;
            float   limbDist = limbDiff.Length();

            if (limbDist < 1.0f)
            {
                //pull the target character to the position of the mouth
                //(+ make the force fluctuate to waggle the character a bit)
                targetCharacter.AnimController.MainLimb.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f));
                targetCharacter.AnimController.MainLimb.body.SmoothRotate(mouthLimb.Rotation);
                targetCharacter.AnimController.Collider.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f));

                //pull the character's mouth to the target character (again with a fluctuating force)
                float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f));
                steeringManager.SteeringManual(deltaTime, limbDiff * pullStrength);
                mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength);

                if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f)
                {
                    //apply damage to the target character to get some blood particles flying
                    targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), 10.0f, false);

                    //keep severing joints until there is only one limb left
                    LimbJoint[] nonSeveredJoints = Array.FindAll(targetCharacter.AnimController.LimbJoints, l => !l.IsSevered);
                    if (nonSeveredJoints.Length == 0)
                    {
                        //only one limb left, the character is now full eaten
                        Entity.Spawner.AddToRemoveQueue(targetCharacter);
                        selectedAiTarget = null;
                        state            = AIState.None;
                    }
                    else //sever a random joint
                    {
                        targetCharacter.AnimController.SeverLimbJoint(nonSeveredJoints[Rand.Int(nonSeveredJoints.Length)]);
                    }
                }
            }
            else if (limbDist < 2.0f)
            {
                steeringManager.SteeringManual(deltaTime, limbDiff);
                Character.AnimController.Collider.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f, mouthPos);
            }
            else
            {
                steeringManager.SteeringSeek(attackSimPosition + (mouthPos - SimPosition), 3);
            }
        }
Example #17
0
        private void DrawWearable(WearableSprite wearable, float depthStep, SpriteBatch spriteBatch, Color color, SpriteEffects spriteEffect)
        {
            if (wearable.InheritSourceRect)
            {
                if (wearable.SheetIndex.HasValue)
                {
                    Point location = (ActiveSprite.SourceRect.Location + ActiveSprite.SourceRect.Size) * wearable.SheetIndex.Value;
                    wearable.Sprite.SourceRect = new Rectangle(location, ActiveSprite.SourceRect.Size);
                }
                else
                {
                    wearable.Sprite.SourceRect = ActiveSprite.SourceRect;
                }
            }

            Vector2 origin = wearable.Sprite.Origin;

            if (wearable.InheritOrigin)
            {
                origin = ActiveSprite.Origin;
                wearable.Sprite.Origin = origin;
            }
            else
            {
                origin = wearable.Sprite.Origin;
                // If the wearable inherits the origin, flipping is already handled.
                if (body.Dir == -1.0f)
                {
                    origin.X = wearable.Sprite.SourceRect.Width - origin.X;
                }
            }

            float depth = wearable.Sprite.Depth;

            if (wearable.InheritLimbDepth)
            {
                depth = ActiveSprite.Depth - depthStep;
                if (wearable.DepthLimb != LimbType.None)
                {
                    Limb depthLimb = character.AnimController.GetLimb(wearable.DepthLimb);
                    if (depthLimb != null)
                    {
                        depth = depthLimb.ActiveSprite.Depth - depthStep;
                    }
                }
            }
            var   wearableItemComponent = wearable.WearableComponent;
            Color wearableColor         = Color.White;

            if (wearableItemComponent != null)
            {
                // Draw outer cloths on top of inner cloths.
                if (wearableItemComponent.AllowedSlots.Contains(InvSlotType.OuterClothes))
                {
                    depth -= depthStep;
                }
                wearableColor = wearableItemComponent.Item.GetSpriteColor();
            }
            float textureScale = wearable.InheritTextureScale ? TextureScale : 1;

            wearable.Sprite.Draw(spriteBatch,
                                 new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
                                 new Color((color.R * wearableColor.R) / (255.0f * 255.0f), (color.G * wearableColor.G) / (255.0f * 255.0f), (color.B * wearableColor.B) / (255.0f * 255.0f)) * ((color.A * wearableColor.A) / (255.0f * 255.0f)),
                                 origin, -body.DrawRotation,
                                 Scale * textureScale, spriteEffect, depth);
        }
Example #18
0
        public static List <Limb> AttachHuskAppendage(Character character, string afflictionIdentifier, XElement appendageDefinition = null, Ragdoll ragdoll = null)
        {
            var appendage = new List <Limb>();

            if (!(AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier == afflictionIdentifier) is AfflictionPrefabHusk matchingAffliction))
            {
                DebugConsole.ThrowError($"Could not find an affliction of type 'huskinfection' that matches the affliction '{afflictionIdentifier}'!");
                return(appendage);
            }
            string nonhuskedSpeciesName = GetNonHuskedSpeciesName(character.SpeciesName, matchingAffliction);
            string huskedSpeciesName    = GetHuskedSpeciesName(nonhuskedSpeciesName, matchingAffliction);
            string filePath             = Character.GetConfigFilePath(huskedSpeciesName);

            if (!Character.TryGetConfigFile(filePath, out XDocument huskDoc))
            {
                DebugConsole.ThrowError($"Error in '{filePath}': Failed to load the config file for the husk infected species with the species name '{huskedSpeciesName}'!");
                return(appendage);
            }
            var mainElement = huskDoc.Root.IsOverride() ? huskDoc.Root.FirstElement() : huskDoc.Root;
            var element     = appendageDefinition;

            if (element == null)
            {
                element = mainElement.GetChildElements("huskappendage").FirstOrDefault(e => e.GetAttributeString("affliction", string.Empty).Equals(afflictionIdentifier));
            }
            if (element == null)
            {
                DebugConsole.ThrowError($"Error in '{filePath}': Failed to find a huskappendage that matches the affliction with an identifier '{afflictionIdentifier}'!");
                return(appendage);
            }
            string    pathToAppendage = element.GetAttributeString("path", string.Empty);
            XDocument doc             = XMLExtensions.TryLoadXml(pathToAppendage);

            if (doc == null)
            {
                return(appendage);
            }
            if (ragdoll == null)
            {
                ragdoll = character.AnimController;
            }
            if (ragdoll.Dir < 1.0f)
            {
                ragdoll.Flip();
            }
            var limbElements = doc.Root.Elements("limb").ToDictionary(e => e.GetAttributeString("id", null), e => e);

            foreach (var jointElement in doc.Root.Elements("joint"))
            {
                if (limbElements.TryGetValue(jointElement.GetAttributeString("limb2", null), out XElement limbElement))
                {
                    var  jointParams = new RagdollParams.JointParams(jointElement, ragdoll.RagdollParams);
                    Limb attachLimb  = null;
                    if (matchingAffliction.AttachLimbId > -1)
                    {
                        attachLimb = ragdoll.Limbs.FirstOrDefault(l => l.Params.ID == matchingAffliction.AttachLimbId);
                    }
                    else if (matchingAffliction.AttachLimbName != null)
                    {
                        attachLimb = ragdoll.Limbs.FirstOrDefault(l => l.Name == matchingAffliction.AttachLimbName);
                    }
                    else if (matchingAffliction.AttachLimbType != LimbType.None)
                    {
                        attachLimb = ragdoll.Limbs.FirstOrDefault(l => l.type == matchingAffliction.AttachLimbType);
                    }
                    if (attachLimb == null)
                    {
                        DebugConsole.Log("Attachment limb not defined in the affliction prefab or no matching limb could be found. Using the appendage definition as it is.");
                        attachLimb = ragdoll.Limbs.FirstOrDefault(l => l.Params.ID == jointParams.Limb1);
                    }
                    if (attachLimb != null)
                    {
                        jointParams.Limb1 = attachLimb.Params.ID;
                        var appendageLimbParams = new RagdollParams.LimbParams(limbElement, ragdoll.RagdollParams)
                        {
                            // Ensure that we have a valid id for the new limb
                            ID = ragdoll.Limbs.Length
                        };
                        jointParams.Limb2 = appendageLimbParams.ID;
                        Limb huskAppendage = new Limb(ragdoll, character, appendageLimbParams);
                        huskAppendage.body.Submarine = character.Submarine;
                        huskAppendage.body.SetTransform(attachLimb.SimPosition, attachLimb.Rotation);
                        ragdoll.AddLimb(huskAppendage);
                        ragdoll.AddJoint(jointParams);
                        appendage.Add(huskAppendage);
                    }
                    else
                    {
                        DebugConsole.ThrowError("Attachment limb not found!");
                    }
                }
            }
            return(appendage);
        }
Example #19
0
        public void Draw(SpriteBatch spriteBatch)
        {
            float brightness = 1.0f - (burnt / 100.0f) * 0.5f;
            Color color      = new Color(brightness, brightness, brightness);

            if (isSevered)
            {
                if (severedFadeOutTimer > SeveredFadeOutTime)
                {
                    return;
                }
                else if (severedFadeOutTimer > SeveredFadeOutTime - 1.0f)
                {
                    color *= SeveredFadeOutTime - severedFadeOutTimer;
                }
            }

            body.Dir = Dir;

            bool hideLimb = wearingItems.Any(w => w != null && w.HideLimb);

            if (!hideLimb)
            {
                body.Draw(spriteBatch, sprite, color, null, scale);
            }
            else
            {
                body.UpdateDrawPosition();
            }

            if (LightSource != null)
            {
                LightSource.Position          = body.DrawPosition;
                LightSource.LightSpriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipVertically;
            }

            foreach (WearableSprite wearable in wearingItems)
            {
                SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally;

                Vector2 origin = wearable.Sprite.Origin;
                if (body.Dir == -1.0f)
                {
                    origin.X = wearable.Sprite.SourceRect.Width - origin.X;
                }

                float depth = wearable.Sprite.Depth;

                if (wearable.InheritLimbDepth)
                {
                    depth = sprite.Depth - 0.000001f;
                    if (wearable.DepthLimb != LimbType.None)
                    {
                        Limb depthLimb = character.AnimController.GetLimb(wearable.DepthLimb);
                        if (depthLimb != null)
                        {
                            depth = depthLimb.sprite.Depth - 0.000001f;
                        }
                    }
                }

                wearable.Sprite.Draw(spriteBatch,
                                     new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
                                     color, origin,
                                     -body.DrawRotation,
                                     scale, spriteEffect, depth);
            }

            if (damage > 0.0f && damagedSprite != null && !hideLimb)
            {
                SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally;

                float depth = sprite.Depth - 0.0000015f;

                damagedSprite.Draw(spriteBatch,
                                   new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
                                   color * Math.Min(damage / 50.0f, 1.0f), sprite.Origin,
                                   -body.DrawRotation,
                                   1.0f, spriteEffect, depth);
            }

            if (!GameMain.DebugDraw)
            {
                return;
            }

            if (pullJoint != null)
            {
                Vector2 pos = ConvertUnits.ToDisplayUnits(pullJoint.WorldAnchorB);
                GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.Red, true);
            }
        }
Example #20
0
        public override void DragCharacter(Character target, float deltaTime)
        {
            if (target == null)
            {
                return;
            }
            Limb mouthLimb = GetLimb(LimbType.Head);

            if (mouthLimb == null)
            {
                return;
            }

            if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
            {
                //stop dragging if there's something between the pull limb and the target
                Vector2 sourceSimPos = SimplePhysicsEnabled ? character.SimPosition : mouthLimb.SimPosition;
                Vector2 targetSimPos = target.SimPosition;
                if (character.Submarine != null && character.SelectedCharacter.Submarine == null)
                {
                    targetSimPos -= character.Submarine.SimPosition;
                }
                else if (character.Submarine == null && character.SelectedCharacter.Submarine != null)
                {
                    sourceSimPos -= character.SelectedCharacter.Submarine.SimPosition;
                }
                var body = Submarine.CheckVisibility(sourceSimPos, targetSimPos, ignoreSubs: true);
                if (body != null)
                {
                    character.DeselectCharacter();
                    return;
                }
            }

            float dmg      = character.Params.EatingSpeed;
            float eatSpeed = dmg / ((float)Math.Sqrt(Math.Max(target.Mass, 1)) * 10);

            eatTimer += deltaTime * eatSpeed;

            Vector2 mouthPos          = SimplePhysicsEnabled ? character.SimPosition : GetMouthPosition().Value;
            Vector2 attackSimPosition = character.Submarine == null?ConvertUnits.ToSimUnits(target.WorldPosition) : target.SimPosition;

            Vector2 limbDiff = attackSimPosition - mouthPos;
            float   extent   = Math.Max(mouthLimb.body.GetMaxExtent(), 1);

            if (limbDiff.LengthSquared() < extent * extent)
            {
                //pull the target character to the position of the mouth
                //(+ make the force fluctuate to waggle the character a bit)
                float dragForce = MathHelper.Clamp(eatSpeed * 10, 0, 40);
                if (dragForce > 0.1f)
                {
                    target.AnimController.MainLimb.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + dragForce));
                    target.AnimController.MainLimb.body.SmoothRotate(mouthLimb.Rotation, dragForce * 2);
                    target.AnimController.Collider.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + dragForce));
                }

                if (InWater)
                {
                    //pull the character's mouth to the target character (again with a fluctuating force)
                    float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f));
                    mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
                }
                else
                {
                    float force = (float)Math.Sin(eatTimer * 100) * mouthLimb.Mass;
                    mouthLimb.body.ApplyLinearImpulse(Vector2.UnitY * force * 2, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
                    mouthLimb.body.ApplyTorque(-force * 50);
                }
                var jaw = GetLimb(LimbType.Jaw);
                if (jaw != null)
                {
                    jaw.body.ApplyTorque(-(float)Math.Sin(eatTimer * 150) * jaw.Mass * 25);
                }

                character.ApplyStatusEffects(ActionType.OnEating, deltaTime);

                float particleFrequency = MathHelper.Clamp(eatSpeed / 2, 0.02f, 0.5f);
                if (Rand.Value() < particleFrequency / 6)
                {
                    target.AnimController.MainLimb.AddDamage(target.SimPosition, dmg, 0, 0, false);
                }
                if (Rand.Value() < particleFrequency)
                {
                    target.AnimController.MainLimb.AddDamage(target.SimPosition, 0, dmg, 0, false);
                }
                if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f)
                {
                    bool CanBeSevered(LimbJoint j) => !j.IsSevered && j.CanBeSevered && j.LimbA != null && !j.LimbA.IsSevered && j.LimbB != null && !j.LimbB.IsSevered;

                    //keep severing joints until there is only one limb left
                    var nonSeveredJoints = target.AnimController.LimbJoints.Where(CanBeSevered);
                    if (nonSeveredJoints.None())
                    {
                        //only one limb left, the character is now full eaten
                        Entity.Spawner?.AddToRemoveQueue(target);
                        character.SelectedCharacter = null;
                    }
                    else //sever a random joint
                    {
                        target.AnimController.SeverLimbJoint(nonSeveredJoints.GetRandom());
                    }
                }
            }
            else
            {
                character.SelectedCharacter = null;
            }
        }
Example #21
0
 public override void Update(CharacterHealth characterHealth, Limb targetLimb, float deltaTime)
 {
     base.Update(characterHealth, targetLimb, deltaTime);
     characterHealth.BloodlossAmount += Strength * (1.0f / 60.0f) * deltaTime;
 }
Example #22
0
        void UpdateSineAnim(float deltaTime)
        {
            if (CurrentSwimParams == null)
            {
                return;
            }
            movement = TargetMovement;

            if (movement.LengthSquared() > 0.00001f)
            {
                float t = 0.5f;
                if (CurrentSwimParams.RotateTowardsMovement && VectorExtensions.Angle(VectorExtensions.Forward(Collider.Rotation + MathHelper.PiOver2), movement) > MathHelper.PiOver2)
                {
                    // Reduce the linear movement speed when not facing the movement direction
                    t /= 5;
                }
                Collider.LinearVelocity = Vector2.Lerp(Collider.LinearVelocity, movement, t);
            }

            //limbs are disabled when simple physics is enabled, no need to move them
            if (SimplePhysicsEnabled)
            {
                return;
            }
            var mainLimb = MainLimb;

            mainLimb.PullJointEnabled = true;
            //mainLimb.PullJointWorldAnchorB = Collider.SimPosition;

            if (movement.LengthSquared() < 0.00001f)
            {
                WalkPos = MathHelper.SmoothStep(WalkPos, MathHelper.PiOver2, deltaTime * 5);
                mainLimb.PullJointWorldAnchorB = Collider.SimPosition;
                return;
            }

            Vector2 transformedMovement = reverse ? -movement : movement;
            float   movementAngle       = MathUtils.VectorToAngle(transformedMovement) - MathHelper.PiOver2;
            float   mainLimbAngle       = 0;

            if (mainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
            {
                mainLimbAngle = TorsoAngle.Value;
            }
            else if (mainLimb.type == LimbType.Head && HeadAngle.HasValue)
            {
                mainLimbAngle = HeadAngle.Value;
            }
            mainLimbAngle *= Dir;
            while (mainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
            {
                movementAngle += MathHelper.TwoPi;
            }
            while (mainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
            {
                movementAngle -= MathHelper.TwoPi;
            }

            if (CurrentSwimParams.RotateTowardsMovement)
            {
                Collider.SmoothRotate(movementAngle, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
                if (TorsoAngle.HasValue)
                {
                    Limb torso = GetLimb(LimbType.Torso);
                    if (torso != null)
                    {
                        SmoothRotateWithoutWrapping(torso, movementAngle + TorsoAngle.Value * Dir, mainLimb, TorsoTorque);
                    }
                }
                if (HeadAngle.HasValue)
                {
                    Limb head = GetLimb(LimbType.Head);
                    if (head != null)
                    {
                        SmoothRotateWithoutWrapping(head, movementAngle + HeadAngle.Value * Dir, mainLimb, HeadTorque);
                    }
                }
                if (TailAngle.HasValue)
                {
                    Limb tail = GetLimb(LimbType.Tail);
                    if (tail != null)
                    {
                        float?mainLimbTargetAngle = null;
                        if (mainLimb.type == LimbType.Torso)
                        {
                            mainLimbTargetAngle = TorsoAngle;
                        }
                        else if (mainLimb.type == LimbType.Head)
                        {
                            mainLimbTargetAngle = HeadAngle;
                        }
                        float torque        = TailTorque;
                        float maxMultiplier = CurrentSwimParams.TailTorqueMultiplier;
                        if (mainLimbTargetAngle.HasValue && maxMultiplier > 1)
                        {
                            float diff   = Math.Abs(mainLimb.Rotation - tail.Rotation);
                            float offset = Math.Abs(mainLimbTargetAngle.Value - TailAngle.Value);
                            torque *= MathHelper.Lerp(1, maxMultiplier, MathUtils.InverseLerp(0, MathHelper.PiOver2, diff - offset));
                        }
                        SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, mainLimb, torque);
                    }
                }
            }
            else
            {
                movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2;
                if (reverse)
                {
                    movementAngle = MathUtils.WrapAngleTwoPi(movementAngle - MathHelper.Pi);
                }
                if (mainLimb.type == LimbType.Head && HeadAngle.HasValue)
                {
                    Collider.SmoothRotate(HeadAngle.Value * Dir, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
                }
                else if (mainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
                {
                    Collider.SmoothRotate(TorsoAngle.Value * Dir, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
                }
                if (TorsoAngle.HasValue)
                {
                    Limb torso = GetLimb(LimbType.Torso);
                    torso?.body.SmoothRotate(TorsoAngle.Value * Dir, TorsoTorque);
                }
                if (HeadAngle.HasValue)
                {
                    Limb head = GetLimb(LimbType.Head);
                    head?.body.SmoothRotate(HeadAngle.Value * Dir, HeadTorque);
                }
                if (TailAngle.HasValue)
                {
                    Limb tail = GetLimb(LimbType.Tail);
                    tail?.body.SmoothRotate(TailAngle.Value * Dir, TailTorque);
                }
            }

            var waveLength    = Math.Abs(CurrentSwimParams.WaveLength * RagdollParams.JointScale);
            var waveAmplitude = Math.Abs(CurrentSwimParams.WaveAmplitude * character.SpeedMultiplier);

            if (waveLength > 0 && waveAmplitude > 0)
            {
                WalkPos -= transformedMovement.Length() / Math.Abs(waveLength);
                WalkPos  = MathUtils.WrapAngleTwoPi(WalkPos);
            }

            foreach (var limb in Limbs)
            {
                if (limb.IsSevered)
                {
                    continue;
                }
                if (Math.Abs(limb.Params.ConstantTorque) > 0)
                {
                    limb.body.SmoothRotate(movementAngle + MathHelper.ToRadians(limb.Params.ConstantAngle) * Dir, limb.Params.ConstantTorque, wrapAngle: true);
                }
                switch (limb.type)
                {
                case LimbType.LeftFoot:
                case LimbType.RightFoot:
                    if (CurrentSwimParams.FootAnglesInRadians.ContainsKey(limb.Params.ID))
                    {
                        SmoothRotateWithoutWrapping(limb, movementAngle + CurrentSwimParams.FootAnglesInRadians[limb.Params.ID] * Dir, mainLimb, FootTorque);
                    }
                    break;

                case LimbType.Tail:
                    if (waveLength > 0 && waveAmplitude > 0)
                    {
                        float waveRotation = (float)Math.Sin(WalkPos);
                        limb.body.ApplyTorque(waveRotation * limb.Mass * waveAmplitude);
                    }
                    break;
                }
            }

            for (int i = 0; i < Limbs.Length; i++)
            {
                var limb = Limbs[i];
                if (limb.IsSevered)
                {
                    continue;
                }
                if (limb.SteerForce <= 0.0f)
                {
                    continue;
                }
                if (!Collider.PhysEnabled)
                {
                    continue;
                }
                Vector2 pullPos = limb.PullJointWorldAnchorA;
                limb.body.ApplyForce(movement * limb.SteerForce * limb.Mass * Math.Max(character.SpeedMultiplier, 1), pullPos);
            }

            Vector2 mainLimbDiff = mainLimb.PullJointWorldAnchorB - mainLimb.SimPosition;

            if (CurrentSwimParams.UseSineMovement)
            {
                mainLimb.PullJointWorldAnchorB = Vector2.SmoothStep(
                    mainLimb.PullJointWorldAnchorB,
                    Collider.SimPosition,
                    mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : (float)Math.Abs(Math.Sin(WalkPos)));
            }
            else
            {
                //mainLimb.PullJointWorldAnchorB = Collider.SimPosition;
                mainLimb.PullJointWorldAnchorB = Vector2.Lerp(
                    mainLimb.PullJointWorldAnchorB,
                    Collider.SimPosition,
                    mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : 0.5f);
            }

            floorY = Limbs[0].SimPosition.Y;
        }
        public override void UpdateAnim(float deltaTime)
        {
            if (Frozen)
            {
                return;
            }
            if (MainLimb == null)
            {
                return;
            }

            if (!character.AllowInput)
            {
                levitatingCollider = false;
                Collider.FarseerBody.FixedRotation = false;
                if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
                {
                    Collider.LinearVelocity            = MainLimb.LinearVelocity;
                    Collider.FarseerBody.FixedRotation = false;
                    Collider.SetTransformIgnoreContacts(MainLimb.SimPosition, MainLimb.Rotation);
                }
                if (character.IsDead && deathAnimTimer < deathAnimDuration)
                {
                    deathAnimTimer += deltaTime;
                    UpdateDying(deltaTime);
                }
                return;
            }
            else
            {
                deathAnimTimer = 0.0f;
            }

            //re-enable collider
            if (!Collider.Enabled)
            {
                var lowestLimb = FindLowestLimb();

                Collider.SetTransform(new Vector2(
                                          Collider.SimPosition.X,
                                          Math.Max(lowestLimb.SimPosition.Y + (Collider.radius + Collider.height / 2), Collider.SimPosition.Y)),
                                      0.0f);

                Collider.Enabled = true;
            }

            ResetPullJoints();

            if (strongestImpact > 0.0f)
            {
                character.Stun  = MathHelper.Clamp(strongestImpact * 0.5f, character.Stun, 5.0f);
                strongestImpact = 0.0f;
            }

            if (inWater && !forceStanding)
            {
                Collider.FarseerBody.FixedRotation = false;
                UpdateSineAnim(deltaTime);
            }
            else if (CanEnterSubmarine && (currentHull != null || forceStanding) && CurrentGroundedParams != null)
            {
                //rotate collider back upright
                float standAngle = dir == Direction.Right ? CurrentGroundedParams.ColliderStandAngleInRadians : -CurrentGroundedParams.ColliderStandAngleInRadians;
                if (Math.Abs(MathUtils.GetShortestAngle(Collider.Rotation, standAngle)) > 0.001f)
                {
                    Collider.AngularVelocity           = MathUtils.GetShortestAngle(Collider.Rotation, standAngle) * 60.0f;
                    Collider.FarseerBody.FixedRotation = false;
                }
                else
                {
                    Collider.FarseerBody.FixedRotation = true;
                }

                UpdateWalkAnim(deltaTime);
            }

            //don't flip or drag when simply physics is enabled
            if (SimplePhysicsEnabled)
            {
                return;
            }

            if (!character.IsRemotePlayer && (character.AIController == null || character.AIController.CanFlip))
            {
                if (!inWater || (CurrentSwimParams != null && CurrentSwimParams.Mirror))
                {
                    if (targetMovement.X > 0.1f && targetMovement.X > Math.Abs(targetMovement.Y) * 0.2f)
                    {
                        TargetDir = Direction.Right;
                    }
                    else if (targetMovement.X < -0.1f && targetMovement.X < -Math.Abs(targetMovement.Y) * 0.2f)
                    {
                        TargetDir = Direction.Left;
                    }
                }
                else
                {
                    float refAngle = 0.0f;
                    Limb  refLimb  = GetLimb(LimbType.Head);
                    if (refLimb == null)
                    {
                        refAngle = CurrentAnimationParams.TorsoAngleInRadians;
                        refLimb  = GetLimb(LimbType.Torso);
                    }
                    else
                    {
                        refAngle = CurrentAnimationParams.HeadAngleInRadians;
                    }

                    float rotation = refLimb.Rotation;
                    if (!float.IsNaN(refAngle))
                    {
                        rotation -= refAngle * Dir;
                    }

                    rotation = MathHelper.ToDegrees(MathUtils.WrapAngleTwoPi(rotation));

                    if (rotation < 0.0f)
                    {
                        rotation += 360;
                    }

                    if (rotation > 20 && rotation < 160)
                    {
                        TargetDir = Direction.Left;
                    }
                    else if (rotation > 200 && rotation < 340)
                    {
                        TargetDir = Direction.Right;
                    }
                }
            }

            if (character.SelectedCharacter != null)
            {
                DragCharacter(character.SelectedCharacter, deltaTime);
            }

            if (!CurrentFishAnimation.Flip || IsStuck)
            {
                return;
            }
            if (character.AIController != null && !character.AIController.CanFlip)
            {
                return;
            }

            flipCooldown -= deltaTime;

            if (TargetDir != Direction.None && TargetDir != dir)
            {
                flipTimer += deltaTime;
                if ((flipTimer > 0.5f && flipCooldown <= 0.0f) || character.IsRemotePlayer)
                {
                    Flip();
                    if (!inWater || (CurrentSwimParams != null && CurrentSwimParams.Mirror))
                    {
                        Mirror();
                    }
                    flipTimer    = 0.0f;
                    flipCooldown = 1.0f;
                }
            }
            else
            {
                flipTimer = 0.0f;
            }
        }
Example #24
0
        void UpdateWalkAnim(float deltaTime)
        {
            movement = MathUtils.SmoothStep(movement, TargetMovement, 0.2f);

            Collider.LinearVelocity = new Vector2(
                movement.X,
                Collider.LinearVelocity.Y > 0.0f ? Collider.LinearVelocity.Y * 0.5f : Collider.LinearVelocity.Y);

            //limbs are disabled when simple physics is enabled, no need to move them
            if (SimplePhysicsEnabled)
            {
                return;
            }

            Vector2 colliderBottom = GetColliderBottom();

            float movementAngle = 0.0f;
            var   mainLimb      = MainLimb;
            float mainLimbAngle = (mainLimb.type == LimbType.Torso ? TorsoAngle ?? 0 : HeadAngle ?? 0) * Dir;

            while (mainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
            {
                movementAngle += MathHelper.TwoPi;
            }
            while (mainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
            {
                movementAngle -= MathHelper.TwoPi;
            }

            float stepLift = TargetMovement.X == 0.0f ? 0 :
                             (float)Math.Sin(WalkPos * CurrentGroundedParams.StepLiftFrequency + MathHelper.Pi * CurrentGroundedParams.StepLiftOffset) * (CurrentGroundedParams.StepLiftAmount / 100);

            float limpAmount = character.GetLegPenalty();

            if (limpAmount > 0)
            {
                float walkPosX = (float)Math.Cos(WalkPos);
                //make the footpos oscillate when limping
                limpAmount = Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * Math.Min(Math.Abs(TargetMovement.X), 0.3f) * Dir;
            }

            Limb torso = GetLimb(LimbType.Torso);

            if (torso != null)
            {
                if (TorsoAngle.HasValue)
                {
                    SmoothRotateWithoutWrapping(torso, movementAngle + TorsoAngle.Value * Dir, mainLimb, TorsoTorque);
                }
                if (TorsoPosition.HasValue)
                {
                    Vector2 pos = colliderBottom + new Vector2(limpAmount, TorsoPosition.Value + stepLift);

                    if (torso != mainLimb)
                    {
                        pos.X = torso.SimPosition.X;
                    }

                    torso.MoveToPos(pos, TorsoMoveForce);
                    torso.PullJointEnabled      = true;
                    torso.PullJointWorldAnchorB = pos;
                }
            }

            Limb head = GetLimb(LimbType.Head);

            if (head != null)
            {
                if (HeadAngle.HasValue)
                {
                    SmoothRotateWithoutWrapping(head, movementAngle + HeadAngle.Value * Dir, mainLimb, HeadTorque);
                }
                if (HeadPosition.HasValue)
                {
                    Vector2 pos = colliderBottom + new Vector2(limpAmount, HeadPosition.Value + stepLift * CurrentGroundedParams.StepLiftHeadMultiplier);

                    if (head != mainLimb)
                    {
                        pos.X = head.SimPosition.X;
                    }

                    head.MoveToPos(pos, HeadMoveForce);
                    head.PullJointEnabled      = true;
                    head.PullJointWorldAnchorB = pos;
                }
            }

            if (TailAngle.HasValue)
            {
                var tail = GetLimb(LimbType.Tail);
                if (tail != null)
                {
                    SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, mainLimb, TailTorque);
                }
            }

            float prevWalkPos = WalkPos;

            WalkPos -= mainLimb.LinearVelocity.X * (CurrentAnimationParams.CycleSpeed / RagdollParams.JointScale / 100.0f);

            Vector2 transformedStepSize = Vector2.Zero;

            if (Math.Abs(TargetMovement.X) > 0.01f)
            {
                transformedStepSize = new Vector2(
                    (float)Math.Cos(WalkPos) * StepSize.Value.X * 3.0f,
                    (float)Math.Sin(WalkPos) * StepSize.Value.Y * 2.0f);
            }

            foreach (Limb limb in Limbs)
            {
                if (limb.IsSevered)
                {
                    continue;
                }
                if (Math.Abs(limb.Params.ConstantTorque) > 0)
                {
                    limb.body.SmoothRotate(movementAngle + MathHelper.ToRadians(limb.Params.ConstantAngle) * Dir, limb.Params.ConstantTorque, wrapAngle: true);
                }
                switch (limb.type)
                {
                case LimbType.LeftFoot:
                case LimbType.RightFoot:
                    Vector2 footPos = new Vector2(limb.SimPosition.X, colliderBottom.Y);

                    if (limb.RefJointIndex > -1)
                    {
                        if (LimbJoints.Length <= limb.RefJointIndex)
                        {
                            DebugConsole.ThrowError($"Reference joint index {limb.RefJointIndex} is out of array. This is probably due to a missing joint. If you just deleted a joint, don't do that without first removing the reference joint indices from the limbs. If this is not the case, please ensure that you have defined the index to the right joint.");
                        }
                        else
                        {
                            footPos.X = LimbJoints[limb.RefJointIndex].WorldAnchorA.X;
                        }
                    }
                    footPos.X += limb.StepOffset.X * Dir;
                    footPos.Y += limb.StepOffset.Y;

                    bool playFootstepSound = false;
                    if (limb.type == LimbType.LeftFoot)
                    {
                        if (Math.Sign(Math.Sin(prevWalkPos)) > 0 && Math.Sign(transformedStepSize.Y) < 0)
                        {
                            playFootstepSound = true;
                        }

                        limb.DebugRefPos    = footPos + Vector2.UnitX * movement.X * 0.1f;
                        limb.DebugTargetPos = footPos + new Vector2(
                            transformedStepSize.X + movement.X * 0.1f,
                            (transformedStepSize.Y > 0.0f) ? transformedStepSize.Y : 0.0f);
                        limb.MoveToPos(limb.DebugTargetPos, FootMoveForce);
                    }
                    else if (limb.type == LimbType.RightFoot)
                    {
                        if (Math.Sign(Math.Sin(prevWalkPos)) < 0 && Math.Sign(transformedStepSize.Y) > 0)
                        {
                            playFootstepSound = true;
                        }

                        limb.DebugRefPos    = footPos + Vector2.UnitX * movement.X * 0.1f;
                        limb.DebugTargetPos = footPos + new Vector2(
                            -transformedStepSize.X + movement.X * 0.1f,
                            (-transformedStepSize.Y > 0.0f) ? -transformedStepSize.Y : 0.0f);
                        limb.MoveToPos(limb.DebugTargetPos, FootMoveForce);
                    }

                    if (playFootstepSound)
                    {
#if CLIENT
                        PlayImpactSound(limb);
#endif
                    }

                    if (CurrentGroundedParams.FootAnglesInRadians.ContainsKey(limb.Params.ID))
                    {
                        SmoothRotateWithoutWrapping(limb,
                                                    movementAngle + CurrentGroundedParams.FootAnglesInRadians[limb.Params.ID] * Dir,
                                                    mainLimb, FootTorque);
                    }
                    break;

                case LimbType.LeftLeg:
                case LimbType.RightLeg:
                    if (Math.Abs(CurrentGroundedParams.LegTorque) > 0)
                    {
                        limb.body.ApplyTorque(limb.Mass * CurrentGroundedParams.LegTorque * Dir);
                    }
                    break;
                }
            }
        }
        void UpdateSineAnim(float deltaTime)
        {
            if (CurrentSwimParams == null)
            {
                return;
            }
            movement = TargetMovement;

            if (movement.LengthSquared() > 0.00001f)
            {
                Collider.LinearVelocity = Vector2.Lerp(Collider.LinearVelocity, movement, 0.5f);
            }

            //limbs are disabled when simple physics is enabled, no need to move them
            if (SimplePhysicsEnabled)
            {
                return;
            }

            MainLimb.PullJointEnabled = true;
            //MainLimb.PullJointWorldAnchorB = Collider.SimPosition;

            if (movement.LengthSquared() < 0.00001f)
            {
                WalkPos = MathHelper.SmoothStep(WalkPos, MathHelper.PiOver2, deltaTime * 5);
                MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
                return;
            }

            Vector2 transformedMovement = reverse ? -movement : movement;
            float   movementAngle       = MathUtils.VectorToAngle(transformedMovement) - MathHelper.PiOver2;
            float   mainLimbAngle       = 0;

            if (MainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
            {
                mainLimbAngle = TorsoAngle.Value;
            }
            else if (MainLimb.type == LimbType.Head && HeadAngle.HasValue)
            {
                mainLimbAngle = HeadAngle.Value;
            }
            mainLimbAngle *= Dir;
            while (MainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
            {
                movementAngle += MathHelper.TwoPi;
            }
            while (MainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
            {
                movementAngle -= MathHelper.TwoPi;
            }

            if (CurrentSwimParams.RotateTowardsMovement)
            {
                Collider.SmoothRotate(movementAngle, CurrentSwimParams.SteerTorque);
                if (TorsoAngle.HasValue)
                {
                    Limb torso = GetLimb(LimbType.Torso);
                    if (torso != null)
                    {
                        SmoothRotateWithoutWrapping(torso, movementAngle + TorsoAngle.Value * Dir, MainLimb, TorsoTorque);
                    }
                }
                if (HeadAngle.HasValue)
                {
                    Limb head = GetLimb(LimbType.Head);
                    if (head != null)
                    {
                        SmoothRotateWithoutWrapping(head, movementAngle + HeadAngle.Value * Dir, MainLimb, HeadTorque);
                    }
                }
                if (TailAngle.HasValue)
                {
                    Limb tail = GetLimb(LimbType.Tail);
                    if (tail != null)
                    {
                        SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, MainLimb, TailTorque);
                    }
                }
            }
            else
            {
                movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2;
                if (reverse)
                {
                    movementAngle = MathUtils.WrapAngleTwoPi(movementAngle - MathHelper.Pi);
                }
                if (MainLimb.type == LimbType.Head && HeadAngle.HasValue)
                {
                    Collider.SmoothRotate(HeadAngle.Value * Dir, CurrentSwimParams.SteerTorque);
                }
                else if (MainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
                {
                    Collider.SmoothRotate(TorsoAngle.Value * Dir, CurrentSwimParams.SteerTorque);
                }
                if (TorsoAngle.HasValue)
                {
                    Limb torso = GetLimb(LimbType.Torso);
                    torso?.body.SmoothRotate(TorsoAngle.Value * Dir, TorsoTorque);
                }
                if (HeadAngle.HasValue)
                {
                    Limb head = GetLimb(LimbType.Head);
                    head?.body.SmoothRotate(HeadAngle.Value * Dir, HeadTorque);
                }
                if (TailAngle.HasValue)
                {
                    Limb tail = GetLimb(LimbType.Tail);
                    tail?.body.SmoothRotate(TailAngle.Value * Dir, TailTorque);
                }
            }

            var waveLength    = Math.Abs(CurrentSwimParams.WaveLength * RagdollParams.JointScale);
            var waveAmplitude = Math.Abs(CurrentSwimParams.WaveAmplitude);

            if (waveLength > 0 && waveAmplitude > 0)
            {
                WalkPos -= transformedMovement.Length() / Math.Abs(waveLength);
                WalkPos  = MathUtils.WrapAngleTwoPi(WalkPos);
            }

            foreach (var limb in Limbs)
            {
                switch (limb.type)
                {
                case LimbType.LeftFoot:
                case LimbType.RightFoot:
                    if (CurrentSwimParams.FootAnglesInRadians.ContainsKey(limb.limbParams.ID))
                    {
                        SmoothRotateWithoutWrapping(limb, movementAngle + CurrentSwimParams.FootAnglesInRadians[limb.limbParams.ID] * Dir, MainLimb, FootTorque);
                    }
                    break;

                case LimbType.Tail:
                    if (waveLength > 0 && waveAmplitude > 0)
                    {
                        float waveRotation = (float)Math.Sin(WalkPos);
                        limb.body.ApplyTorque(waveRotation * limb.Mass * CurrentSwimParams.TailTorque * waveAmplitude);
                    }
                    break;
                }
            }

            for (int i = 0; i < Limbs.Length; i++)
            {
                if (Limbs[i].SteerForce <= 0.0f)
                {
                    continue;
                }

                Vector2 pullPos = Limbs[i].PullJointWorldAnchorA;
                Limbs[i].body.ApplyForce(movement * Limbs[i].SteerForce * Limbs[i].Mass, pullPos);
            }

            Vector2 mainLimbDiff = MainLimb.PullJointWorldAnchorB - MainLimb.SimPosition;

            if (CurrentSwimParams.UseSineMovement)
            {
                MainLimb.PullJointWorldAnchorB = Vector2.SmoothStep(
                    MainLimb.PullJointWorldAnchorB,
                    Collider.SimPosition,
                    mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : (float)Math.Abs(Math.Sin(WalkPos)));
            }
            else
            {
                //MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
                MainLimb.PullJointWorldAnchorB = Vector2.Lerp(
                    MainLimb.PullJointWorldAnchorB,
                    Collider.SimPosition,
                    mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : 0.5f);
            }

            floorY = Limbs[0].SimPosition.Y;
        }
Example #26
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);
            }
        }
        public virtual void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
        {
            if (GameMain.Server == null)
            {
                return;
            }

            if (extraData != null)
            {
                switch ((NetEntityEvent.Type)extraData[0])
                {
                case NetEntityEvent.Type.InventoryState:
                    msg.WriteRangedInteger(0, 0, 5);
                    msg.Write(GameMain.Server.EntityEventManager.Events.Last()?.ID ?? (ushort)0);
                    Inventory.ServerWrite(msg, c);
                    break;

                case NetEntityEvent.Type.Control:
                    msg.WriteRangedInteger(1, 0, 5);
                    Client owner = (Client)extraData[1];
                    msg.Write(owner != null && owner.Character == this && GameMain.Server.ConnectedClients.Contains(owner) ? owner.ID : (byte)0);
                    break;

                case NetEntityEvent.Type.Status:
                    msg.WriteRangedInteger(2, 0, 5);
                    WriteStatus(msg);
                    break;

                case NetEntityEvent.Type.UpdateSkills:
                    msg.WriteRangedInteger(3, 0, 5);
                    if (Info?.Job == null)
                    {
                        msg.Write((byte)0);
                    }
                    else
                    {
                        msg.Write((byte)Info.Job.Skills.Count);
                        foreach (Skill skill in Info.Job.Skills)
                        {
                            msg.Write(skill.Identifier);
                            msg.Write(skill.Level);
                        }
                    }
                    break;

                case NetEntityEvent.Type.ExecuteAttack:
                    Limb   attackLimb      = extraData[1] as Limb;
                    UInt16 targetEntityID  = (UInt16)extraData[2];
                    int    targetLimbIndex = extraData.Length > 3 ? (int)extraData[3] : 0;
                    msg.WriteRangedInteger(4, 0, 5);
                    msg.Write((byte)(Removed ? 255 : Array.IndexOf(AnimController.Limbs, attackLimb)));
                    msg.Write(targetEntityID);
                    msg.Write((byte)targetLimbIndex);
                    break;

                case NetEntityEvent.Type.AssignCampaignInteraction:
                    msg.WriteRangedInteger(5, 0, 5);
                    msg.Write((byte)CampaignInteractionType);
                    break;

                default:
                    DebugConsole.ThrowError("Invalid NetworkEvent type for entity " + ToString() + " (" + (NetEntityEvent.Type)extraData[0] + ")");
                    break;
                }
                msg.WritePadBits();
            }
            else
            {
                msg.Write(ID);

                IWriteMessage tempBuffer = new WriteOnlyMessage();

                if (this == c.Character)
                {
                    tempBuffer.Write(true);
                    if (LastNetworkUpdateID < memInput.Count + 1)
                    {
                        tempBuffer.Write((UInt16)0);
                    }
                    else
                    {
                        tempBuffer.Write((UInt16)(LastNetworkUpdateID - memInput.Count - 1));
                    }
                }
                else
                {
                    tempBuffer.Write(false);

                    bool aiming = false;
                    bool use    = false;
                    bool attack = false;
                    bool shoot  = false;

                    if (IsRemotePlayer)
                    {
                        aiming = dequeuedInput.HasFlag(InputNetFlags.Aim);
                        use    = dequeuedInput.HasFlag(InputNetFlags.Use);
                        attack = dequeuedInput.HasFlag(InputNetFlags.Attack);
                        shoot  = dequeuedInput.HasFlag(InputNetFlags.Shoot);
                    }
                    else if (keys != null)
                    {
                        aiming            = keys[(int)InputType.Aim].GetHeldQueue;
                        use               = keys[(int)InputType.Use].GetHeldQueue;
                        attack            = keys[(int)InputType.Attack].GetHeldQueue;
                        shoot             = keys[(int)InputType.Shoot].GetHeldQueue;
                        networkUpdateSent = true;
                    }

                    tempBuffer.Write(aiming);
                    tempBuffer.Write(shoot);
                    tempBuffer.Write(use);
                    if (AnimController is HumanoidAnimController)
                    {
                        tempBuffer.Write(((HumanoidAnimController)AnimController).Crouching);
                    }
                    tempBuffer.Write(attack);

                    Vector2 relativeCursorPos = cursorPosition - AimRefPosition;
                    tempBuffer.Write((UInt16)(65535.0 * Math.Atan2(relativeCursorPos.Y, relativeCursorPos.X) / (2.0 * Math.PI)));

                    tempBuffer.Write(IsRagdolled || Stun > 0.0f || IsDead || IsIncapacitated);

                    tempBuffer.Write(AnimController.Dir > 0.0f);
                }

                if (SelectedCharacter != null || SelectedConstruction != null)
                {
                    tempBuffer.Write(true);
                    tempBuffer.Write(SelectedCharacter != null ? SelectedCharacter.ID : NullEntityID);
                    tempBuffer.Write(SelectedConstruction != null ? SelectedConstruction.ID : NullEntityID);
                    if (SelectedCharacter != null)
                    {
                        tempBuffer.Write(AnimController.Anim == AnimController.Animation.CPR);
                    }
                }
                else
                {
                    tempBuffer.Write(false);
                }

                tempBuffer.Write(SimPosition.X);
                tempBuffer.Write(SimPosition.Y);
                float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
                AnimController.Collider.LinearVelocity = new Vector2(
                    MathHelper.Clamp(AnimController.Collider.LinearVelocity.X, -MaxVel, MaxVel),
                    MathHelper.Clamp(AnimController.Collider.LinearVelocity.Y, -MaxVel, MaxVel));
                tempBuffer.WriteRangedSingle(AnimController.Collider.LinearVelocity.X, -MaxVel, MaxVel, 12);
                tempBuffer.WriteRangedSingle(AnimController.Collider.LinearVelocity.Y, -MaxVel, MaxVel, 12);

                bool fixedRotation = AnimController.Collider.FarseerBody.FixedRotation || !AnimController.Collider.PhysEnabled;
                tempBuffer.Write(fixedRotation);
                if (!fixedRotation)
                {
                    tempBuffer.Write(AnimController.Collider.Rotation);
                    float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
                    AnimController.Collider.AngularVelocity = NetConfig.Quantize(AnimController.Collider.AngularVelocity, -MaxAngularVel, MaxAngularVel, 8);
                    tempBuffer.WriteRangedSingle(MathHelper.Clamp(AnimController.Collider.AngularVelocity, -MaxAngularVel, MaxAngularVel), -MaxAngularVel, MaxAngularVel, 8);
                }

                bool writeStatus = healthUpdateTimer <= 0.0f;
                tempBuffer.Write(writeStatus);
                if (writeStatus)
                {
                    WriteStatus(tempBuffer);
                    HealthUpdatePending = false;
                }

                tempBuffer.WritePadBits();

                msg.WriteVariableUInt32((uint)tempBuffer.LengthBytes);
                msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
            }
        }
Example #28
0
 public LimbJoint(Limb limbA, Limb limbB, JointParams jointParams, Ragdoll ragdoll) : this(limbA, limbB, Vector2.One, Vector2.One)
 {
     Params       = jointParams;
     this.ragdoll = ragdoll;
     LoadParams();
 }
Example #29
0
        public static void UpdateAll(float deltaTime)
        {
            UpdateAllProjSpecific(deltaTime);

            DelayedEffect.Update(deltaTime);
            for (int i = DurationList.Count - 1; i >= 0; i--)
            {
                DurationListElement element = DurationList[i];

                if (element.Parent.CheckConditionalAlways && !element.Parent.HasRequiredConditions(element.Targets))
                {
                    DurationList.RemoveAt(i);
                    continue;
                }

                element.Targets.RemoveAll(t =>
                                          (t is Entity entity && entity.Removed) ||
                                          (t is Limb limb && (limb.character == null || limb.character.Removed)));
                if (element.Targets.Count == 0)
                {
                    DurationList.RemoveAt(i);
                    continue;
                }

                foreach (ISerializableEntity target in element.Targets)
                {
                    for (int n = 0; n < element.Parent.propertyNames.Length; n++)
                    {
                        if (target == null ||
                            target.SerializableProperties == null ||
                            !target.SerializableProperties.TryGetValue(element.Parent.propertyNames[n], out SerializableProperty property))
                        {
                            continue;
                        }
                        element.Parent.ApplyToProperty(target, property, element.Parent.propertyEffects[n], CoroutineManager.UnscaledDeltaTime);
                    }

                    foreach (Affliction affliction in element.Parent.Afflictions)
                    {
                        Affliction multipliedAffliction = affliction;
                        if (!element.Parent.disableDeltaTime)
                        {
                            multipliedAffliction = affliction.CreateMultiplied(deltaTime);
                        }

                        if (target is Character character)
                        {
                            character.AddDamage(character.WorldPosition, new List <Affliction>()
                            {
                                multipliedAffliction
                            }, stun: 0.0f, playSound: false);
                        }
                        else if (target is Limb limb)
                        {
                            limb.character.DamageLimb(limb.WorldPosition, limb, new List <Affliction>()
                            {
                                multipliedAffliction
                            }, stun: 0.0f, playSound: false, attackImpulse: 0.0f);
                        }
                    }

                    foreach (Pair <string, float> reduceAffliction in element.Parent.ReduceAffliction)
                    {
                        Limb      targetLimb      = null;
                        Character targetCharacter = null;
                        if (target is Character character)
                        {
                            targetCharacter = character;
                        }
                        else if (target is Limb limb)
                        {
                            targetLimb      = limb;
                            targetCharacter = limb.character;
                        }
                        if (targetCharacter != null)
                        {
                            float prevVitality = targetCharacter.Vitality;
                            targetCharacter.CharacterHealth.ReduceAffliction(targetLimb, reduceAffliction.First, reduceAffliction.Second * deltaTime);
#if SERVER
                            GameMain.Server.KarmaManager.OnCharacterHealthChanged(targetCharacter, element.Parent.user, prevVitality - targetCharacter.Vitality);
#endif
                        }
                    }
                }

                element.Timer -= deltaTime;

                if (element.Timer > 0.0f)
                {
                    continue;
                }
                DurationList.Remove(element);
            }
        }
Example #30
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);
                            }
                        }
                    }
                }
            }
        }