コード例 #1
0
ファイル: TaskKill.cs プロジェクト: LilianaMoore/CryoFall
 public static TaskKill Require(
     IProtoCharacterMob protoCreature,
     ushort count       = 1,
     string description = null)
 {
     return(new TaskKill(protoCreature, count, description));
 }
コード例 #2
0
        public static void ServerProcessMinions(
            ICharacter characterBoss,
            IProtoCharacterMob protoMinion,
            List <ICharacter> minionsList,
            out int spawnedMinionsCount,
            double despawnDistanceSqr)
        {
            var bossPosition = characterBoss.Position;

            spawnedMinionsCount = 0;

            for (var index = minionsList.Count - 1; index >= 0; index--)
            {
                var minion = minionsList[index];
                if (minion.GetPublicState <CharacterMobPublicState>().IsDead)
                {
                    //Logger.Dev("The minion is dead: " + minion);
                    minionsList.RemoveAt(index);
                    continue;
                }

                if (minion.Position.DistanceSquaredTo(bossPosition)
                    >= despawnDistanceSqr)
                {
                    //Logger.Dev("The minion is too far, despawn it: " + minion);
                    protoMinion.ServerSetSpawnState(minion, MobSpawnState.Despawning);
                    minionsList.RemoveAt(index);
                    continue;
                }

                spawnedMinionsCount++;
            }
        }
コード例 #3
0
 private static void ServerNotifyCharactersNotEligibleForReward(
     IProtoCharacterMob protoCharacterBoss,
     List <ICharacter> list)
 {
     Instance.CallClient(list,
                         _ => _.ClientRemote_BossRewardNotAvailable(
                             protoCharacterBoss));
 }
コード例 #4
0
        public string Execute(IProtoCharacterMob protoMob, ushort x, ushort y)
        {
            var worldOffset = ServerWorldService.WorldBounds.Offset;

            x += worldOffset.X;
            y += worldOffset.Y;

            return(ServerSpawn(protoMob, (x, y)));
        }
コード例 #5
0
ファイル: TaskKill.cs プロジェクト: LilianaMoore/CryoFall
 private TaskKill(
     IProtoCharacterMob protoCharacterMob,
     ushort count,
     string description)
     : base(count,
            description)
 {
     this.ProtoCharacterMob = protoCharacterMob;
 }
コード例 #6
0
        private static int GetLevel(IProtoCharacterMob character)
        {
            if (character is null)
            {
                return(1);
            }

            if (character.IsBoss && Api.IsServer && (SharedLocalServerHelper.IsLocalServer || Api.IsEditor))
            {
                return(1);
            }

            return(RandomLevels[RandomHelper.Next(0, RandomLevels.Count)]);
        }
コード例 #7
0
        public string Execute(
            IProtoCharacterMob protoMob,
            byte count = 1,
            [CurrentCharacterIfNull] ICharacter nearPlayer = null)
        {
            var result = new StringBuilder();

            for (var i = 0; i < count; i++)
            {
                result.AppendLine(ServerSpawn(protoMob, nearPlayer.Position));
            }

            return(result.ToString());
        }
コード例 #8
0
 private static void ServerNotifyCharactersEligibleForReward(
     IProtoCharacterMob protoCharacterBoss,
     List <WinnerEntry> list,
     ushort totalLootCount)
 {
     foreach (var entry in list)
     {
         Instance.CallClient(entry.Character,
                             _ => _.ClientRemote_BossRewardAvailable(
                                 protoCharacterBoss,
                                 entry.DamagePercent,
                                 entry.LootCount,
                                 totalLootCount));
     }
 }
コード例 #9
0
        private static string ServerSpawn(IProtoCharacterMob protoMob, Vector2D position)
        {
            position = FindClosestPosition(position);

            var character = Server.Characters.SpawnCharacter(
                protoMob,
                position);

            if (character == null)
            {
                throw new Exception("Cannot spawn character.");
            }

            return($"{character} spawned at {position}.");
        }
コード例 #10
0
ファイル: ObjectCorpse.cs プロジェクト: LilianaMoore/CryoFall
        public override float StructurePointsMax => 9001; // non-damageable

        public static void ServerSetupCorpse(
            IStaticWorldObject objectCorpse,
            IProtoCharacterMob protoCharacterMob,
            Vector2F tileOffset,
            bool isFlippedHorizontally)
        {
            var publicState = GetPublicState(objectCorpse);

            publicState.ProtoCharacterMob     = protoCharacterMob;
            publicState.TileOffset            = tileOffset;
            publicState.IsFlippedHorizontally = isFlippedHorizontally;
            publicState.DeathTime             = Server.Game.FrameTime;

            // re-initialize the object physics
            // (it's required because the physics should use a proper tile offset we've just set)
            objectCorpse.ProtoStaticWorldObject.SharedCreatePhysics(objectCorpse);
        }
コード例 #11
0
        /// <summary>
        /// Server spawn callback for mob.
        /// </summary>
        /// <param name="trigger">Trigger leading to this spawn.</param>
        /// <param name="zone">Server zone instance.</param>
        /// <param name="protoMob">Prototype of character mob object to spawn.</param>
        /// <param name="tilePosition">Position to try spawn at.</param>
        protected virtual IGameObjectWithProto ServerSpawnMob(
            IProtoTrigger trigger,
            IServerZone zone,
            IProtoCharacterMob protoMob,
            Vector2Ushort tilePosition)
        {
            var worldPosition = tilePosition.ToVector2D();

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

            return(Server.Characters.SpawnCharacter(
                       protoMob,
                       worldPosition));
        }
コード例 #12
0
        private static string ServerSpawn(IProtoCharacterMob protoMob, Vector2D desiredPosition)
        {
            var position = FindClosestPosition(desiredPosition);

            if (!position.HasValue)
            {
                return("No empty position available nearby.");
            }

            var character = Server.Characters.SpawnCharacter(
                protoMob,
                position.Value);

            if (character is null)
            {
                throw new Exception("Cannot spawn character.");
            }

            return($"{character} spawned at {position.Value}.");
        }
コード例 #13
0
        public override float StructurePointsMax => 0; // non-damageable

        public static void ServerSetupCorpse(
            IStaticWorldObject objectCorpse,
            uint forDeadCharacterId,
            IProtoCharacterMob protoCharacterMob,
            Vector2F tileOffset,
            bool isFlippedHorizontally)
        {
            var publicState = GetPublicState(objectCorpse);

            publicState.ProtoCharacterMob     = protoCharacterMob;
            publicState.TileOffset            = tileOffset;
            publicState.IsFlippedHorizontally = isFlippedHorizontally;
            publicState.DeathTime             = Server.Game.FrameTime;
            publicState.DeadCharacterId       = forDeadCharacterId;

            // re-initialize the object physics
            // (it's required because the physics should use
            // a proper tile offset and CorpseInteractionAreaScale from the mob prototype)
            objectCorpse.ProtoStaticWorldObject.SharedCreatePhysics(objectCorpse);
        }
コード例 #14
0
        public string Execute(
            IProtoCharacterMob protoMob,
            ushort x,
            ushort y,
            byte count = 1)
        {
            var worldOffset = ServerWorldService.WorldBounds.Offset;

            x += worldOffset.X;
            y += worldOffset.Y;

            var result = new StringBuilder();

            for (var i = 0; i < count; i++)
            {
                result.AppendLine(ServerSpawn(protoMob, (x, y)));
            }

            return(result.ToString());
        }
コード例 #15
0
        public static void SetLevel(IProtoCharacterMob protoCharacter, ICharacter character, CharacterMobPublicState publicState, CharacterMobPrivateState privateState)
        {
            if (publicState is null)
            {
                return;
            }

            int level;

            //check the zone
            int levelByZone = GetLevelByZone(character.TilePosition);

            if (levelByZone != 0)
            {
                level = levelByZone;
            }
            else
            {
                //check if there is a parent mob
                var list = character.PhysicsBody.PhysicsSpace.TestCircle(character.TilePosition.ToVector2D(), 10.0, CollisionGroups.Default).AsList()
                           .Where(t => t.PhysicsBody.AssociatedWorldObject is not null)
                           .Where(t => t.PhysicsBody.AssociatedWorldObject.ProtoWorldObject is IProtoCharacterBoss ||
                                  t.PhysicsBody.AssociatedWorldObject.ProtoWorldObject is IProtoCharacterSmallBoss).ToList();

                level = GetLevel(protoCharacter);

                if (list.Count > 0)
                {
                    var mobPublicState = list[0].PhysicsBody.AssociatedWorldObject.GetPublicState <CharacterMobPublicState>();
                    if (mobPublicState is not null && level > mobPublicState.Level)
                    {
                        level = mobPublicState.Level;
                    }
                }
            }

            publicState.Level = level;
        }
コード例 #16
0
ファイル: TaskDefeatBoss.cs プロジェクト: peter-r-g/CryoFall
        private void BossDefeatedHandler(
            IProtoCharacterMob protoCharacterBoss,
            Vector2Ushort bossPosition,
            List <ServerBossLootSystem.WinnerEntry> winnerEntries)
        {
            if (protoCharacterBoss != this.ProtoCharacterMob)
            {
                // different boss type
                return;
            }

            foreach (var winner in winnerEntries)
            {
                var activeContext = this.GetActiveContext(winner.Character, out var state);
                if (activeContext is null)
                {
                    continue;
                }

                state.SetCountCurrent(state.CountCurrent + 1, this.RequiredCount);
                activeContext.Refresh();
            }
        }
コード例 #17
0
        private static void ServerBossDefeatedHandler(
            IProtoCharacterMob protoCharacterBoss,
            Vector2Ushort bossPosition,
            List <ServerBossLootSystem.WinnerEntry> winnerEntries)
        {
            var lootByFaction = winnerEntries.GroupBy(e => FactionSystem.SharedGetClanTag(e.Character))
                                .Where(g => !string.IsNullOrEmpty(g.Key))
                                .ToDictionary(g => g.Key, g => g.Sum(l => l.LootCount));

            foreach (var pair in lootByFaction)
            {
                var clanTag = pair.Key;
                var faction = FactionSystem.ServerGetFactionByClanTag(clanTag);
                if (faction is null)
                {
                    Api.Logger.Error("Should be impossible - no faction for clan tag: " + clanTag);
                    continue;
                }

                var factionPrivateState = Faction.GetPrivateState(faction);
                factionPrivateState.ServerMetricBossScore += (ulong)pair.Value;
                /*Logger.Dev("Boss metric updated: " + factionPrivateState.ServerMetricBossScore);*/
            }
        }
コード例 #18
0
 public ProtoCharacterMobViewModel([NotNull] IProtoCharacterMob creature) : base(creature)
 {
 }
コード例 #19
0
        public static void ServerSpawnMinions(
            ICharacter characterBoss,
            Vector2D characterBossCenterPosition,
            IProtoCharacterMob protoMinion,
            List <ICharacter> minionsList,
            double spawnCheckDistanceSqr,
            ServerBossDamageTracker bossDamageTracker,
            double minionsPerPlayer,
            int minionsTotalMin,
            int minionsTotalMax,
            int?minionsSpawnPerIterationLimit,
            double baseMinionsNumber,
            double spawnNoObstaclesCircleRadius,
            double spawnDistanceMin,
            double spawnDistanceMax)
        {
            // calculate how many minions required
            var minionsRequired = baseMinionsNumber;

            using var tempListCharacters = Api.Shared.GetTempList <ICharacter>();
            Api.Server.World.GetScopedByPlayers(characterBoss, tempListCharacters);

            foreach (var player in tempListCharacters.AsList())
            {
                if (player.Position.DistanceSquaredTo(characterBossCenterPosition)
                    <= spawnCheckDistanceSqr)
                {
                    minionsRequired += minionsPerPlayer;
                }
            }

            minionsRequired = MathHelper.Clamp(minionsRequired,
                                               minionsTotalMin,
                                               minionsTotalMax);

            if (minionsSpawnPerIterationLimit.HasValue)
            {
                minionsRequired = Math.Min(minionsRequired, minionsSpawnPerIterationLimit.Value);
            }

            ServerProcessMinions(characterBoss,
                                 protoMinion,
                                 minionsList,
                                 out var spawnedMinionsCount,
                                 despawnDistanceSqr: spawnCheckDistanceSqr);

            //Logger.Dev($"Minions required: {minionsRequired} minions have: {minionsHave}");
            minionsRequired -= spawnedMinionsCount;
            if (minionsRequired <= 0)
            {
                return;
            }

            // spawn minions
            var attemptsRemains = 300;
            var physicsSpace    = characterBoss.PhysicsBody.PhysicsSpace;

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

                var spawnDistance = spawnDistanceMin
                                    + RandomHelper.NextDouble() * (spawnDistanceMax - spawnDistanceMin);
                var angle         = RandomHelper.NextDouble() * MathConstants.DoublePI;
                var spawnPosition = new Vector2Ushort(
                    (ushort)(characterBossCenterPosition.X + spawnDistance * Math.Cos(angle)),
                    (ushort)(characterBossCenterPosition.Y + spawnDistance * Math.Sin(angle)));

                if (ServerTrySpawnMinion(spawnPosition) is { } spawnedMinion)
                {
                    // spawned successfully!
                    minionsRequired--;
                    minionsList.Add(spawnedMinion);
                }
            }

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

                if (physicsSpace.TestCircle(worldPosition,
                                            spawnNoObstaclesCircleRadius,
                                            CollisionGroups.Default,
                                            sendDebugEvent: true).EnumerateAndDispose().Any())
                {
                    // obstacles nearby
                    return(null);
                }

                var spawnedCharacter = Api.Server.Characters.SpawnCharacter(protoMinion, worldPosition);

                if (spawnedCharacter is null)
                {
                    return(null);
                }

                // write this boss' damage tracker into the minion character
                // so any damage dealt to it will be counted in the winners ranking
                var privateState = spawnedCharacter.GetPrivateState <ICharacterPrivateStateWithBossDamageTracker>();

                privateState.DamageTracker = bossDamageTracker;

                // start spawn animation
                if (spawnedCharacter.ProtoGameObject is IProtoCharacterMob protoCharacterMob)
                {
                    protoCharacterMob.ServerSetSpawnState(spawnedCharacter,
                                                          MobSpawnState.Spawning);
                }

                return(spawnedCharacter);
            }
        }
コード例 #20
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);
            }
        }
コード例 #21
0
 public void ServerOnMobKilled(IProtoCharacterMob protoMob)
 {
     Api.ValidateIsServer();
     AddIfNotContains(protoMob, this.ListMobs);
 }
コード例 #22
0
 public string Execute(IProtoCharacterMob protoMob, [CurrentCharacterIfNull] ICharacter nearPlayer = null)
 {
     return(ServerSpawn(protoMob, nearPlayer.Position));
 }