예제 #1
0
 public override void ClientOnWeaponHitOrTrace(
     ICharacter firingCharacter,
     Vector2D worldPositionSource,
     IProtoItemWeapon protoWeapon,
     IProtoItemAmmo protoAmmo,
     IProtoCharacter protoCharacter,
     in Vector2Ushort fallbackCharacterPosition,
예제 #2
0
 public static void ServerTrySpawnMobsCustom(
     IProtoCharacter protoMob,
     int countToSpawn,
     RectangleInt excludeBounds,
     int maxSpawnDistanceFromExcludeBounds,
     double noObstaclesCheckRadius,
     int maxAttempts)
 {
     using var tempList = Api.Shared.GetTempList <ICharacter>();
     ServerTrySpawnMobsCustom(protoMob,
                              spawnedCollection: tempList.AsList(),
                              countToSpawn,
                              excludeBounds,
                              maxSpawnDistanceFromExcludeBounds,
                              noObstaclesCheckRadius,
                              maxAttempts);
 }
예제 #3
0
        private void ClientRemote_OnWeaponShot(
            ICharacter whoFires,
            IProtoItemWeapon protoWeapon,
            IProtoCharacter fallbackProtoCharacter,
            Vector2D fallbackPosition)
        {
            if (whoFires != null &&
                !whoFires.IsInitialized)
            {
                whoFires = null;
            }

            WeaponSystemClientDisplay.OnWeaponShot(whoFires,
                                                   protoWeapon,
                                                   fallbackProtoCharacter,
                                                   fallbackPosition);
        }
예제 #4
0
 private void ClientRemote_OnWeaponHitOrTrace(
     ICharacter firingCharacter,
     IProtoItemWeapon protoWeapon,
     IProtoItemAmmo protoAmmo,
     IProtoCharacter protoCharacter,
     Vector2Ushort fallbackCharacterPosition,
     WeaponHitData[] hitObjects,
     Vector2D endPosition,
     bool endsWithHit)
 {
     WeaponSystemClientDisplay.ClientOnWeaponHitOrTrace(firingCharacter,
                                                        protoWeapon,
                                                        protoAmmo,
                                                        protoCharacter,
                                                        fallbackCharacterPosition,
                                                        hitObjects,
                                                        endPosition,
                                                        endsWithHit);
 }
예제 #5
0
        public static void ClientOnWeaponHitOrTrace(
            ICharacter firingCharacter,
            IProtoItemWeapon protoWeapon,
            IProtoItemAmmo protoAmmo,
            IProtoCharacter protoCharacter,
            Vector2Ushort fallbackCharacterPosition,
            IReadOnlyList <WeaponHitData> hitObjects,
            Vector2D endPosition,
            bool endsWithHit)
        {
            if (firingCharacter is not null &&
                !firingCharacter.IsInitialized)
            {
                firingCharacter = null;
            }

            var weaponTracePreset = protoWeapon.FireTracePreset
                                    ?? protoAmmo?.FireTracePreset;
            var worldPositionSource = SharedCalculateWeaponShotWorldPositon(
                firingCharacter,
                protoWeapon,
                protoCharacter,
                fallbackCharacterPosition.ToVector2D(),
                hasTrace: weaponTracePreset?.HasTrace ?? false);

            protoWeapon.ClientOnWeaponHitOrTrace(firingCharacter,
                                                 worldPositionSource,
                                                 protoWeapon,
                                                 protoAmmo,
                                                 protoCharacter,
                                                 fallbackCharacterPosition,
                                                 hitObjects,
                                                 endPosition,
                                                 endsWithHit);

            if (weaponTracePreset?.HasTrace ?? false)
            {
                ComponentWeaponTrace.Create(weaponTracePreset,
                                            worldPositionSource,
                                            endPosition,
                                            hasHit: endsWithHit,
                                            lastHitData: hitObjects.LastOrDefault(t => t.WorldObject is not null));
            }

            foreach (var hitData in hitObjects)
            {
                var hitWorldObject = hitData.WorldObject;
                if (hitWorldObject is not null &&
                    !hitWorldObject.IsInitialized)
                {
                    hitWorldObject = null;
                }

                var protoWorldObject = hitData.FallbackProtoWorldObject;

                double delay;
                {
                    var worldObjectPosition = CalculateWorldObjectPosition(hitWorldObject, hitData);
                    delay = weaponTracePreset?.HasTrace ?? false
                                ? SharedCalculateTimeToHit(weaponTracePreset,
                                                           worldPositionSource : worldPositionSource,
                                                           endPosition : worldObjectPosition
                                                           + hitData.HitPoint.ToVector2D())
                                : 0;
                }

                ClientTimersSystem.AddAction(
                    delay,
                    () =>
                {
                    // re-calculate the world object position
                    var worldObjectPosition = CalculateWorldObjectPosition(hitWorldObject, hitData);

                    var fireScatterPreset = protoAmmo?.OverrideFireScatterPreset
                                            ?? protoWeapon.FireScatterPreset;
                    var projectilesCount = fireScatterPreset.ProjectileAngleOffets.Length;

                    var objectMaterial = hitData.FallbackObjectMaterial;
                    if (hitWorldObject is ICharacter hitCharacter &&
                        hitCharacter.IsInitialized)
                    {
                        objectMaterial = ((IProtoCharacterCore)hitCharacter.ProtoCharacter)
                                         .SharedGetObjectMaterialForCharacter(hitCharacter);
                    }

                    protoWeapon.ClientPlayWeaponHitSound(hitWorldObject,
                                                         protoWorldObject,
                                                         fireScatterPreset,
                                                         objectMaterial,
                                                         worldObjectPosition);

                    if (weaponTracePreset is not null)
                    {
                        ClientAddHitSparks(weaponTracePreset.HitSparksPreset,
                                           hitData,
                                           hitWorldObject,
                                           protoWorldObject,
                                           worldObjectPosition,
                                           projectilesCount,
                                           objectMaterial,
                                           randomizeHitPointOffset: !weaponTracePreset.HasTrace,
                                           randomRotation: !weaponTracePreset.HasTrace,
                                           drawOrder: weaponTracePreset.DrawHitSparksAsLight
                                                              ? DrawOrder.Light
                                                              : DrawOrder.Default);
                    }
                });
예제 #6
0
        public static void ServerTrySpawnMobsCustom(
            IProtoCharacter protoMob,
            ICollection <ICharacter> spawnedCollection,
            int countToSpawn,
            RectangleInt excludeBounds,
            int maxSpawnDistanceFromExcludeBounds,
            double noObstaclesCheckRadius,
            int maxAttempts)
        {
            if (countToSpawn <= 0)
            {
                return;
            }

            var spawnBounds = excludeBounds.Inflate(maxSpawnDistanceFromExcludeBounds,
                                                    maxSpawnDistanceFromExcludeBounds);
            var physicsSpace = Api.Server.World.GetPhysicsSpace();

            while (maxAttempts-- > 0)
            {
                var position = new Vector2D(spawnBounds.Left + RandomHelper.NextDouble() * spawnBounds.Width,
                                            spawnBounds.Bottom + RandomHelper.NextDouble() * spawnBounds.Height);
                if (IsTooClose(position))
                {
                    continue;
                }

                var character = ServerTrySpawnMob(position);
                if (character is null)
                {
                    // cannot spawn there
                    continue;
                }

                spawnedCollection.Add(character);

                countToSpawn--;
                if (countToSpawn == 0)
                {
                    return;
                }
            }

            bool IsTooClose(in Vector2D position)
            => position.X >= excludeBounds.X &&
            position.Y >= excludeBounds.Y &&
            position.X < excludeBounds.X + excludeBounds.Width &&
            position.Y < excludeBounds.Y + excludeBounds.Height;

            ICharacter ServerTrySpawnMob(Vector2D worldPosition)
            {
                foreach (var _ in physicsSpace.TestCircle(worldPosition,
                                                          radius: noObstaclesCheckRadius,
                                                          CollisionGroups.Default,
                                                          sendDebugEvent: false).EnumerateAndDispose())
                {
                    // position is not valid for spawning
                    return(null);
                }

                if (!ServerCharacterSpawnHelper.IsValidSpawnTile(
                        Api.Server.World.GetTile(worldPosition.ToVector2Ushort()),
                        checkNeighborTiles: true))
                {
                    return(null);
                }

                return(Api.Server.Characters.SpawnCharacter(protoMob,
                                                            worldPosition));
            }
        }
예제 #7
0
        public static void ServerSpawnBossMinionsOnDeath(
            Vector2Ushort epicenterPosition,
            double bossDifficultyCoef,
            IProtoCharacter minionProto,
            int minionsDefaultCount,
            double minionsRadius)
        {
            var countToSpawnRemains = minionsDefaultCount;

            // apply difficulty coefficient
            countToSpawnRemains = (int)Math.Ceiling(countToSpawnRemains * bossDifficultyCoef);

            var attemptsRemains = 3000;

            while (countToSpawnRemains > 0)
            {
                attemptsRemains--;
                if (attemptsRemains <= 0)
                {
                    // attempts exceeded
                    return;
                }

                // calculate random distance from the explosion epicenter
                var distance = RandomHelper.Range(2, minionsRadius);

                // ensure we spawn more objects closer to the epicenter
                var spawnProbability = 1 - (distance / minionsRadius);
                spawnProbability = Math.Pow(spawnProbability, 1.25);
                if (!RandomHelper.RollWithProbability(spawnProbability))
                {
                    // random skip
                    continue;
                }

                var angle         = RandomHelper.NextDouble() * MathConstants.DoublePI;
                var spawnPosition = new Vector2Ushort(
                    (ushort)(epicenterPosition.X + distance * Math.Cos(angle)),
                    (ushort)(epicenterPosition.Y + distance * Math.Sin(angle)));

                if (ServerTrySpawnMinion(spawnPosition))
                {
                    // spawned successfully!
                    countToSpawnRemains--;
                }
            }

            bool ServerTrySpawnMinion(Vector2Ushort spawnPosition)
            {
                var worldPosition = spawnPosition.ToVector2D();

                if (!ServerCharacterSpawnHelper.IsPositionValidForCharacterSpawn(worldPosition,
                                                                                 isPlayer: false))
                {
                    // position is not valid for spawning
                    return(false);
                }

                var spawnedCharacter = ServerCharacters.SpawnCharacter(minionProto, worldPosition);

                return(spawnedCharacter is not null);
            }
        }
        public static void OnWeaponShot(
            ICharacter character,
            IProtoItemWeapon protoWeapon,
            IProtoCharacter fallbackProtoCharacter,
            Vector2D fallbackPosition)
        {
            var position = character?.Position ?? fallbackPosition;

            position += (0, fallbackProtoCharacter.CharacterWorldWeaponOffsetRanged);

            ClientSoundCueManager.OnSoundEvent(position);

            const float volume = SoundConstants.VolumeWeapon;
            var         pitch  = RandomHelper.Range(0.95f, 1.05f);

            IComponentSoundEmitter emitter;
            var soundPresetWeapon = protoWeapon.SoundPresetWeapon;

            if (soundPresetWeapon.HasSound(WeaponSound.Shot))
            {
                // play shot sound from weapon
                if (character != null)
                {
                    soundPresetWeapon.PlaySound(WeaponSound.Shot,
                                                character,
                                                out emitter,
                                                volume: volume,
                                                pitch: pitch);
                }
                else
                {
                    soundPresetWeapon.PlaySound(WeaponSound.Shot,
                                                protoWorldObject: fallbackProtoCharacter,
                                                worldPosition: position,
                                                out emitter,
                                                volume: volume,
                                                pitch: pitch);
                }
            }
            else
            {
                // play sounds from the skeleton instead
                ProtoCharacterSkeleton characterSkeleton = null;
                if (character != null)
                {
                    var clientState = character.GetClientState <BaseCharacterClientState>();
                    if (clientState.HasWeaponAnimationAssigned)
                    {
                        characterSkeleton = clientState.CurrentProtoSkeleton;
                    }
                }
                else
                {
                    fallbackProtoCharacter.SharedGetSkeletonProto(character: null,
                                                                  out var characterSkeleton1,
                                                                  out _);
                    characterSkeleton = (ProtoCharacterSkeleton)characterSkeleton1;
                }

                if (characterSkeleton == null)
                {
                    emitter = null;
                }
                else
                {
                    if (character != null)
                    {
                        if (!characterSkeleton.SoundPresetWeapon.PlaySound(WeaponSound.Shot,
                                                                           character,
                                                                           out emitter,
                                                                           volume))
                        {
                            // no method returned true
                            // fallback to the default weapon sound (if there is no, it will be logged into the audio log)
                            soundPresetWeapon.PlaySound(WeaponSound.Shot,
                                                        character,
                                                        out emitter,
                                                        volume: volume,
                                                        pitch: pitch);
                        }
                    }
                    else if (!characterSkeleton.SoundPresetWeapon.PlaySound(WeaponSound.Shot,
                                                                            protoWorldObject: fallbackProtoCharacter,
                                                                            worldPosition: position,
                                                                            out emitter,
                                                                            volume))
                    {
                        // no method returned true
                        // fallback to the default weapon sound (if there is no, it will be logged into the audio log)
                        soundPresetWeapon.PlaySound(WeaponSound.Shot,
                                                    protoWorldObject: fallbackProtoCharacter,
                                                    worldPosition: position,
                                                    out emitter,
                                                    volume: volume,
                                                    pitch: pitch);
                    }
                }
            }

            if (emitter != null)
            {
                var distance = protoWeapon.SoundPresetWeaponDistance;
                emitter.CustomMinDistance = distance.min;
                emitter.CustomMaxDistance = distance.max;
            }

            if (protoWeapon is IProtoItemWeaponRanged rangedWeapon &&
                character != null &&
                ReferenceEquals(protoWeapon, GetCharacterCurrentWeaponProto(character)))
            {
                // add muzzle flash
                var clientState = character.GetClientState <BaseCharacterClientState>();
                if (!clientState.HasWeaponAnimationAssigned)
                {
                    return;
                }

                var skeletonRenderer = clientState.SkeletonRenderer;
                CreateMuzzleFlash(rangedWeapon, character, skeletonRenderer);

                var recoilAnimationName = rangedWeapon.CharacterAnimationAimingRecoilName;
                if (recoilAnimationName != null &&
                    rangedWeapon.CharacterAnimationAimingRecoilPower > 0 &&
                    rangedWeapon.CharacterAnimationAimingRecoilDuration > 0)
                {
                    SetRecoilAnimation(
                        character,
                        rangedWeapon,
                        skeletonRenderer);
                }
            }
        }