Beispiel #1
0
        private void ClientExplodeAt(WeaponFinalCache weaponCache, Vector2D explosionWorldPosition)
        {
            var protoWeapon        = weaponCache.ProtoWeapon;
            var shotSourcePosition = WeaponSystemClientDisplay.SharedCalculateWeaponShotWorldPositon(
                weaponCache.Character,
                protoWeapon,
                weaponCache.Character.ProtoCharacter,
                weaponCache.Character.Position,
                hasTrace: true);

            this.ClientExplodeAt(protoWeapon, shotSourcePosition, explosionWorldPosition);
        }
Beispiel #2
0
        private void ClientExplodeAt(
            IProtoItemWeapon protoWeapon,
            Vector2D shotSourcePosition,
            Vector2D explosionWorldPosition)
        {
            var timeToHit = WeaponSystemClientDisplay.SharedCalculateTimeToHit(protoWeapon.FireTracePreset
                                                                               ?? this.FireTracePreset,
                                                                               shotSourcePosition,
                                                                               explosionWorldPosition);

            ClientTimersSystem.AddAction(timeToHit,
                                         () => ExplosionHelper.ClientExplode(explosionWorldPosition,
                                                                             this.ExplosionPreset,
                                                                             this.ExplosionVolume));
        }
Beispiel #3
0
        private void ServerCreateDroppedArrow(WeaponFinalCache weaponCache, Vector2D endPosition)
        {
            var shotSourcePosition = WeaponSystemClientDisplay.SharedCalculateWeaponShotWorldPositon(
                weaponCache.Character,
                weaponCache.ProtoWeapon,
                weaponCache.Character.ProtoCharacter,
                weaponCache.Character.Position,
                hasTrace: true);

            var timeToHit = WeaponSystemClientDisplay.SharedCalculateTimeToHit(
                weaponCache.ProtoWeapon.FireTracePreset
                ?? weaponCache.ProtoAmmo.FireTracePreset,
                shotSourcePosition,
                endPosition);

            ServerTimersSystem.AddAction(
                timeToHit,
                () =>
            {
                var droplist        = ServerGetDroplistFor(weaponCache.ProtoAmmo);
                var endTilePosition = endPosition.ToVector2Ushort();

                var result = ServerDroplistHelper.TryDropToGround(
                    droplist,
                    endTilePosition,
                    // compensate for the server rate to ensure that
                    // it doesn't affect the number of arrows spawned
                    probabilityMultiplier: 1.0 / DropItemsList.DropListItemsCountMultiplier,
                    context: new DropItemContext(weaponCache.Character),
                    out _);

                if (!result.IsEverythingCreated)
                {
                    return;
                }

                using var charactersObserving = Api.Shared.GetTempList <ICharacter>();
                Server.World.GetCharactersInRadius(endTilePosition,
                                                   charactersObserving,
                                                   radius: 15,
                                                   onlyPlayers: true);

                this.CallClient(charactersObserving.AsList(),
                                _ => _.ClientRemote_OnArrowHitGround(endTilePosition));
            });
        }
Beispiel #4
0
        private void ClientRemote_OnArrowHitGround(Vector2Ushort endTilePosition)
        {
            var hitPosition = new Vector2D(endTilePosition.X + 0.5,
                                           endTilePosition.Y + 0.5);

            WeaponSystemClientDisplay.ClientAddHitSparks(
                WeaponHitSparksPresets.Firearm,
                new WeaponHitData(hitPosition: hitPosition),
                hitWorldObject: null,
                protoWorldObject: null,
                worldObjectPosition: endTilePosition.ToVector2D(),
                projectilesCount: 1,
                objectMaterial: ObjectMaterial.Wood,
                randomizeHitPointOffset: false,
                randomRotation: false,
                drawOrder: DrawOrder.Light);
        }
Beispiel #5
0
        protected override void ClientExplodeAt(
            IProtoItemWeapon protoWeapon,
            Vector2D shotSourcePosition,
            Vector2D explosionWorldPosition)
        {
            var timeToHit = WeaponSystemClientDisplay.SharedCalculateTimeToHit(protoWeapon.FireTracePreset
                                                                               ?? this.FireTracePreset,
                                                                               shotSourcePosition,
                                                                               explosionWorldPosition);

            ClientTimersSystem.AddAction(
                timeToHit,
                () =>
            {
                SharedExplosionHelper.ClientExplode(explosionWorldPosition,
                                                    this.ExplosionPreset,
                                                    this.VolumeExplosion);

                // add more explosions in the X pattern as it's a fragmentation grenade
                ClientTimersSystem.AddAction(
                    0.2,
                    () =>
                {
                    SharedExplosionHelper.ClientExplode(explosionWorldPosition + (1, 1),
                                                        this.ExplosionPreset,
                                                        volume: 0);

                    SharedExplosionHelper.ClientExplode(explosionWorldPosition + (1, -1),
                                                        this.ExplosionPreset,
                                                        volume: 0);

                    SharedExplosionHelper.ClientExplode(explosionWorldPosition + (-1, 1),
                                                        this.ExplosionPreset,
                                                        volume: 0);

                    SharedExplosionHelper.ClientExplode(explosionWorldPosition + (-1, -1),
                                                        this.ExplosionPreset,
                                                        volume: 0);
                });
Beispiel #6
0
        private void ServerExplodeAt(WeaponFinalCache weaponCache, Vector2D endPosition, bool isHit)
        {
            var character   = weaponCache.Character;
            var protoWeapon = (IProtoItemWeaponRanged)weaponCache.ProtoWeapon;

            var shotSourcePosition = WeaponSystemClientDisplay.SharedCalculateWeaponShotWorldPositon(
                character,
                protoWeapon,
                character.ProtoCharacter,
                character.Position,
                hasTrace: true);

            if (isHit)
            {
                // offset end position a bit towards the source position
                // this way the explosion will definitely happen outside the hit object
                endPosition -= 0.1 * (endPosition - shotSourcePosition).Normalized;
            }

            var timeToHit = WeaponSystemClientDisplay.SharedCalculateTimeToHit(
                protoWeapon.FireTracePreset
                ?? this.FireTracePreset,
                shotSourcePosition,
                endPosition);

            // We're using a check here similar to the one in WeaponSystem.
            // Ensure that player can hit objects only on the same height level
            // and can fire through over the pits (the cliffs of the lower heights).
            var anyCliffIsAnObstacle = character.Tile.Height
                                       != Server.World.GetTile(endPosition.ToVector2Ushort()).Height;

            if (!WeaponSystem.SharedHasTileObstacle(
                    character.Position,
                    character.Tile.Height,
                    endPosition,
                    character.PhysicsBody.PhysicsSpace,
                    anyCliffIsAnObstacle: anyCliffIsAnObstacle))
            {
                ServerTimersSystem.AddAction(
                    timeToHit,
                    () =>
                {
                    ExplosionHelper.ServerExplode(
                        character: character,
                        protoExplosive: this,
                        protoWeapon: protoWeapon,
                        explosionPreset: this.ExplosionPreset,
                        epicenterPosition: endPosition,
                        damageDescriptionCharacters: this.DamageDescription,
                        physicsSpace: Server.World.GetPhysicsSpace(),
                        executeExplosionCallback: this.ServerExecuteExplosion);
                });
            }

            // notify other characters about the explosion
            using var charactersObserving = Api.Shared.GetTempList <ICharacter>();
            var explosionEventRadius = Math.Max(protoWeapon.SoundPresetWeaponDistance.max,
                                                this.FireRangeMax)
                                       + this.DamageRadius;

            Server.World.GetCharactersInRadius(endPosition.ToVector2Ushort(),
                                               charactersObserving,
                                               radius: (byte)Math.Min(byte.MaxValue, explosionEventRadius),
                                               onlyPlayers: true);

            charactersObserving.Remove(character);

            this.CallClient(charactersObserving.AsList(),
                            _ => _.ClientRemote_OnExplosion(protoWeapon,
                                                            shotSourcePosition,
                                                            endPosition));
        }
Beispiel #7
0
        public override void Update(double deltaTime)
        {
            if (this.dronePublicState.TargetObjectPosition.HasValue)
            {
                this.lastTargetPosition = this.dronePublicState.TargetObjectPosition.Value.ToVector2D();

                if (this.hitWorldObject is null)
                {
                    this.hitWorldObject = Client.World.GetTile(this.lastTargetPosition.Value.ToVector2Ushort())
                                          .StaticObjects
                                          .FirstOrDefault();
                }

                if (this.hitWorldObject != null)
                {
                    var objectCenter = DroneTargetPositionHelper.GetTargetPosition(this.hitWorldObject);
                    objectCenter             = (objectCenter.X, objectCenter.Y * 0.5 + 0.2);
                    this.lastTargetPosition += objectCenter;
                }
            }
            else
            {
                this.hitWorldObject = null;
            }

            if (this.hitWorldObject != null &&
                this.soundEmitterMiningProcess.SoundResource is null)
            {
                this.soundEmitterMiningProcess.SoundResource
                    = GetSourceResourceMiningProcess(this.hitWorldObject.ProtoStaticWorldObject);
            }

            var wasEnabled   = this.spriteRendererLine.IsEnabled;
            var isBeamActive = this.dronePublicState.IsMining &&
                               this.lastTargetPosition.HasValue;

            this.alpha = MathHelper.LerpWithDeltaTime(
                this.alpha,
                isBeamActive ? 1 : 0,
                deltaTime: deltaTime,
                rate: 20);

            var isEnabled = this.alpha > 0.001;

            this.spriteRendererLine.IsEnabled        = isEnabled;
            this.spriteRendererOrigin.IsEnabled      = isEnabled;
            this.soundEmitterMiningProcess.IsEnabled = isEnabled;

            if (!isEnabled)
            {
                if (wasEnabled)
                {
                    // just disabled
                    Api.Client.Audio.PlayOneShot(SourceResourceMiningEnd,
                                                 this.SceneObject,
                                                 MiningVolume);
                }

                return;
            }

            if (!wasEnabled)
            {
                // just enabled
                Api.Client.Audio.PlayOneShot(SourceResourceMiningStart,
                                             this.SceneObject,
                                             MiningVolume);
            }

            var alphaComponent = (byte)(this.alpha * byte.MaxValue);

            this.spriteRendererLine.Color         = this.beamColor.WithAlpha(alphaComponent);
            this.spriteRendererOrigin.Color       = Color.FromArgb(alphaComponent, 0xFF, 0xFF, 0xFF);
            this.soundEmitterMiningProcess.Volume = (float)(this.alpha * MiningVolume);

            // update line start-end positions and rotation angle
            var currentBeamOriginOffset = this.beamOriginOffset
                                          + this.primaryRenderer.PositionOffset
                                          - this.primaryRendererDefaultPositionOffset;

            var lineStartWorldPosition = this.SceneObject.Position + currentBeamOriginOffset;

            // ReSharper disable once PossibleInvalidOperationException
            var lineEndWorldPosition = this.lastTargetPosition.Value;

            lineEndWorldPosition += this.GetMiningTargetMovementAnimation(deltaTime);

            var lineDirection = lineEndWorldPosition - lineStartWorldPosition;

            this.spriteRendererLine.PositionOffset   = currentBeamOriginOffset;
            this.spriteRendererLine.RotationAngleRad = (float)-Math.Atan2(lineDirection.Y, lineDirection.X);
            this.spriteRendererLine.Scale            = (lineDirection.Length, this.beamWidth);
            this.spriteRendererLine.DrawOrderOffsetY = this.primaryRenderer.DrawOrderOffsetY;

            this.spriteRendererOrigin.PositionOffset   = currentBeamOriginOffset;
            this.spriteRendererOrigin.Scale            = this.beamWidth;
            this.spriteRendererOrigin.DrawOrderOffsetY = this.primaryRenderer.DrawOrderOffsetY;

            if (this.hitWorldObject is null)
            {
                return;
            }

            this.timeSinceLastHitSparks -= deltaTime;
            if (this.timeSinceLastHitSparks < 0)
            {
                WeaponSystemClientDisplay.ClientAddHitSparks(
                    WeaponHitSparksPresets.LaserMining,
                    new WeaponHitData(Vector2D.Zero),
                    this.hitWorldObject,
                    this.hitWorldObject.ProtoStaticWorldObject,
                    worldObjectPosition: lineEndWorldPosition,
                    projectilesCount: 1,
                    objectMaterial: this.hitWorldObject.ProtoStaticWorldObject.SharedGetObjectMaterial(),
                    randomizeHitPointOffset: false,
                    randomRotation: true,
                    drawOrder: DrawOrder.Light + 2,
                    animationFrameDuration: 3 / 60.0);
                this.timeSinceLastHitSparks += HitSparksInterval;
            }
        }
        public static void ClientUpdateAnimation(
            ICharacter character,
            BaseCharacterClientState clientState,
            ICharacterPublicState publicState)
        {
            var skeletonRenderer = clientState.SkeletonRenderer;

            if (skeletonRenderer == null)
            {
                return;
            }

            var activeWeaponProto = publicState.CurrentItemWeaponProto;

            var protoSkeleton  = clientState.CurrentProtoSkeleton;
            var rendererShadow = clientState.RendererShadow;
            var wasDead        = clientState.IsDead;

            clientState.IsDead = publicState.IsDead;

            if (publicState.IsDead)
            {
                if (skeletonRenderer.GetCurrentAnimationName(AnimationTrackIndexes.Primary)
                    == "Death")
                {
                    // already in death animation
                    return;
                }

                // character was not dead on client and now become dead
                skeletonRenderer.ResetSkeleton();

                skeletonRenderer.SelectCurrentSkeleton(
                    protoSkeleton.SkeletonResourceFront,
                    "Idle",
                    isLooped: false);

                skeletonRenderer.SetAnimation(AnimationTrackIndexes.Primary, "Death", isLooped: false);

                if (!wasDead.HasValue)
                {
                    // character entered scope and it's dead
                    skeletonRenderer.SetAnimationTime(0, 10000);
                    // hide skeleton completely
                    HideBody();
                    return;
                }

                // character just died
                // play death sound
                protoSkeleton.PlaySound(CharacterSound.Death, character);
                clientState.SoundEmitterLoopCharacter.Stop();
                clientState.SoundEmitterLoopMovemement.Stop();

                // hide skeleton after timeout
                ClientTimersSystem.AddAction(
                    CorpseTimeoutSeconds,
                    HideBody);

                void HideBody()
                {
                    if (!publicState.IsDead)
                    {
                        // the character has been respawned
                        return;
                    }

                    skeletonRenderer.IsEnabled = false;
                    rendererShadow.IsEnabled   = false;
                }

                return;
            }

            skeletonRenderer.IsEnabled = true;
            rendererShadow.IsEnabled   = true;

            var appliedInput     = publicState.AppliedInput;
            var rotationAngleRad = GetCurrentRotationAngleRadInterpolated(character,
                                                                          clientState,
                                                                          appliedInput);

            GetCurrentAnimationSetting(
                protoSkeleton,
                appliedInput.MoveModes,
                rotationAngleRad,
                clientState.LastViewOrientation,
                out var newAnimationStarterName,
                out var newAnimationName,
                out var currentDrawMode,
                out var aimCoef,
                out var viewOrientation,
                out var isIdle);

            clientState.LastViewOrientation = viewOrientation;

            // TODO: consider adding new field - HasBackwardAnimations
            if (!protoSkeleton.HasMoveStartAnimations)
            {
                newAnimationStarterName = null;
                if (newAnimationName == "RunSideBackward")
                {
                    newAnimationName = "RunSide";
                }
            }

            var currentSkeleton = viewOrientation.IsUp && protoSkeleton.SkeletonResourceBack != null
                                      ? protoSkeleton.SkeletonResourceBack
                                      : protoSkeleton.SkeletonResourceFront;

            if (skeletonRenderer.CurrentSkeleton != currentSkeleton)
            {
                // switch skeleton completely
                skeletonRenderer.SelectCurrentSkeleton(
                    currentSkeleton,
                    // please note: no starter animation in that case!
                    animationName: newAnimationName,
                    isLooped: true);
            }
            else
            {
                var activeAnimationName =
                    skeletonRenderer.GetLatestAddedAnimationName(trackIndex: AnimationTrackIndexes.Primary);
                if (newAnimationName != activeAnimationName &&
                    newAnimationStarterName != activeAnimationName)
                {
                    //Api.Logger.WriteDev(
                    //	newAnimationStarterName != null
                    //	? $"Changing move animation: {activeAnimationName}->{newAnimationStarterName}->{newAnimationName}"
                    //	: $"Changing move animation: {activeAnimationName}->{newAnimationName}");

                    var hasStarterAnimation = newAnimationStarterName != null;
                    if (hasStarterAnimation)
                    {
                        // add starter animation
                        skeletonRenderer.SetAnimation(
                            trackIndex: AnimationTrackIndexes.Primary,
                            animationName: newAnimationStarterName,
                            isLooped: false);

                        // add looped animation
                        skeletonRenderer.AddAnimation(
                            trackIndex: AnimationTrackIndexes.Primary,
                            animationName: newAnimationName,
                            isLooped: true);
                    }
                    else if (newAnimationName == "Idle" &&
                             skeletonRenderer.GetCurrentAnimationName(trackIndex: AnimationTrackIndexes.Primary)
                             .EndsWith("Start"))
                    {
                        // going into idle when playing a start animation - allow to finish it!
                        // remove queued entries
                        skeletonRenderer.RemoveAnimationTrackNextEntries(trackIndex: AnimationTrackIndexes.Primary);

                        // add looped idle animation
                        skeletonRenderer.AddAnimation(
                            trackIndex: AnimationTrackIndexes.Primary,
                            animationName: newAnimationName,
                            isLooped: true);
                    }
                    else
                    {
                        // set looped animation
                        skeletonRenderer.SetAnimation(
                            trackIndex: AnimationTrackIndexes.Primary,
                            animationName: newAnimationName,
                            isLooped: true);
                    }
                }
            }

            if (appliedInput.MoveModes != CharacterMoveModes.None)
            {
                // moving mode
                var animationSpeedMultiplier = appliedInput.MoveSpeed
                                               / (protoSkeleton.DefaultMoveSpeed
                                                  * clientState.CurrentProtoSkeletonScaleMultiplier);

                skeletonRenderer.SetAnimationSpeed(
                    trackIndex: AnimationTrackIndexes.Primary,
                    speedMultiliper: (float)animationSpeedMultiplier);

                // moving - remove animation for static firing
                skeletonRenderer.RemoveAnimationTrack(AnimationTrackIndexes.ItemFiringStatic);
            }
            else
            {
                skeletonRenderer.SetAnimationSpeed(
                    trackIndex: AnimationTrackIndexes.Primary,
                    speedMultiliper: 1.0f);
            }

            skeletonRenderer.DrawMode = currentDrawMode;

            if (activeWeaponProto != null)
            {
                clientState.HasWeaponAnimationAssigned = true;
                clientState.LastAimCoef = aimCoef;

                var aimingAnimationName = activeWeaponProto.CharacterAnimationAimingName;
                if (aimingAnimationName != null)
                {
                    //Api.Logger.WriteDev(
                    //    $"Setting aiming animation: {aimingAnimationName} timePercents: {aimCoef:F2}");
                    skeletonRenderer.SetAnimationFrame(
                        trackIndex: AnimationTrackIndexes.ItemAiming,
                        animationName: aimingAnimationName,
                        timePositionPercents: aimCoef);
                }
                else
                {
                    skeletonRenderer.RemoveAnimationTrack(AnimationTrackIndexes.ItemAiming);
                }

                WeaponSystemClientDisplay.RefreshCurrentAttackAnimation(character);
            }
            else
            {
                clientState.HasWeaponAnimationAssigned = false;
            }

            SetLoopSounds(character, clientState, publicState.AppliedInput, protoSkeleton, isIdle);
        }