protected override void ServerOnStaticObjectZeroStructurePoints(
            WeaponFinalCache weaponCache,
            ICharacter byCharacter,
            IWorldObject targetObject)
        {
            var tilePosition = targetObject.TilePosition;

            base.ServerOnStaticObjectZeroStructurePoints(weaponCache, byCharacter, targetObject);
            ServerWorld.CreateStaticWorldObject <ObjectMineralPragmiumSourceExplosion>(tilePosition);
        }
Exemple #2
0
        public virtual void ServerOnDeath(ICharacter character)
        {
            this.ServerSendDeathSoundEvent(character);

            ServerWorld.DestroyObject(character);

            var position          = character.Position;
            var tilePosition      = position.ToVector2Ushort();
            var rotationAngleRad  = character.GetPublicState <ICharacterPublicState>().AppliedInput.RotationAngleRad;
            var isLeftOrientation = ClientCharacterAnimationHelper.IsLeftHalfOfCircle(
                angleDeg: rotationAngleRad * MathConstants.RadToDeg);
            var isFlippedHorizontally = !isLeftOrientation;

            var objectCorpse = ServerWorld.CreateStaticWorldObject <ObjectCorpse>(tilePosition);

            ObjectCorpse.ServerSetupCorpse(objectCorpse,
                                           (IProtoCharacterMob)character.ProtoCharacter,
                                           (Vector2F)(position - tilePosition.ToVector2D()),
                                           isFlippedHorizontally: isFlippedHorizontally);
        }
Exemple #3
0
        public static void ServerCreateBossLoot(
            Vector2Ushort bossPosition,
            IProtoCharacterMob protoCharacterBoss,
            ServerBossDamageTracker damageTracker,
            double bossDifficultyCoef,
            IProtoStaticWorldObject lootObjectProto,
            int lootObjectsDefaultCount,
            double lootObjectsRadius,
            double learningPointsBonusPerLootObject,
            int maxLootWinners)
        {
            var approximatedTotalLootCountToSpawn = (int)Math.Ceiling(lootObjectsDefaultCount * bossDifficultyCoef);

            if (approximatedTotalLootCountToSpawn < 2)
            {
                approximatedTotalLootCountToSpawn = 2;
            }

            var allCharactersByDamage = damageTracker.GetDamageByCharacter();
            var winners = ServerSelectWinnersByParticipation(
                ServerSelectWinnersByDamage(allCharactersByDamage,
                                            maxLootWinners));

            var winnerEntries = winners
                                .Select(
                c => new WinnerEntry(
                    c.Character,
                    damagePercent: (byte)Math.Max(
                        1,
                        Math.Round(c.Score * 100, MidpointRounding.AwayFromZero)),
                    lootCount: CalculateLootCountForScore(c.Score)))
                                .ToList();

            if (PveSystem.ServerIsPvE)
            {
                ServerNotifyCharactersNotEligibleForReward(
                    protoCharacterBoss,
                    allCharactersByDamage.Select(c => c.Character)
                    .Except(winners.Select(c => c.Character))
                    .ToList());

                ServerNotifyCharactersEligibleForReward(
                    protoCharacterBoss,
                    winnerEntries,
                    totalLootCount: (ushort)winnerEntries.Sum(e => (int)e.LootCount));
            }

            ServerSpawnLoot();

            // send victory announcement notification
            var winnerNamesWithClanTags = winnerEntries
                                          .Select(w => (Name: w.Character.Name,
                                                        ClanTag: FactionSystem.SharedGetClanTag(w.Character)))
                                          .ToArray();

            Instance.CallClient(
                ServerCharacters.EnumerateAllPlayerCharacters(onlyOnline: true),
                _ => _.ClientRemote_VictoryAnnouncement(protoCharacterBoss,
                                                        winnerNamesWithClanTags));

            foreach (var entry in winnerEntries)
            {
                // provide bonus LP
                entry.Character.SharedGetTechnologies()
                .ServerAddLearningPoints(
                    learningPointsBonusPerLootObject * entry.LootCount,
                    allowModifyingByStatsAndRates: false);
            }

            Api.Logger.Important(
                protoCharacterBoss.ShortId
                + " boss defeated."
                + Environment.NewLine
                + "Damage by player:"
                + Environment.NewLine
                + allCharactersByDamage.Select(p => $" * {p.Character}: {p.Damage:F0}")
                .GetJoinedString(Environment.NewLine)
                + Environment.NewLine
                + "Player participation score: (only for selected winners)"
                + Environment.NewLine
                + winners.Select(p => $" * {p.Character}: {(p.Score * 100):F1}%")
                .GetJoinedString(Environment.NewLine));

            Api.SafeInvoke(
                () => BossDefeated?.Invoke(protoCharacterBoss,
                                           bossPosition,
                                           winnerEntries));

            byte CalculateLootCountForScore(double score)
            {
                var result = Math.Ceiling(approximatedTotalLootCountToSpawn * score);

                if (result < 1)
                {
                    return(1);
                }

                return((byte)Math.Min(byte.MaxValue, result));
            }

            void ServerSpawnLoot()
            {
                foreach (var winnerEntry in winnerEntries)
                {
                    if (!ServerTrySpawnLootForWinner(winnerEntry.Character, winnerEntry.LootCount))
                    {
                        // spawn attempts failure as logged inside the method,
                        // abort further spawning
                        return;
                    }
                }
            }

            bool ServerTrySpawnLootForWinner(ICharacter forCharacter, double countToSpawnRemains)
            {
                var attemptsRemains = 2000;

                while (countToSpawnRemains > 0)
                {
                    attemptsRemains--;
                    if (attemptsRemains <= 0)
                    {
                        // attempts exceeded
                        Api.Logger.Error(
                            "Cannot spawn all the loot for boss - number of attempts exceeded. Cont to spawn for winner remains: "
                            + countToSpawnRemains);
                        return(false);
                    }

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

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

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

                    if (ServerTrySpawnLootObject(spawnPosition, forCharacter))
                    {
                        // spawned successfully!
                        countToSpawnRemains--;
                    }
                }

                return(true);
            }

            bool ServerTrySpawnLootObject(Vector2Ushort spawnPosition, ICharacter forCharacter)
            {
                if (!lootObjectProto.CheckTileRequirements(spawnPosition,
                                                           character: null,
                                                           logErrors: false))
                {
                    return(false);
                }

                var lootObject = ServerWorld.CreateStaticWorldObject(lootObjectProto, spawnPosition);

                if (lootObject is null)
                {
                    return(false);
                }

                // mark the loot object for this player (works only in PvE)
                WorldObjectClaimSystem.ServerTryClaim(lootObject,
                                                      forCharacter,
                                                      WorldObjectClaimDuration.BossLoot,
                                                      claimForPartyMembers: false);
                return(true);
            }
        }