Exemple #1
0
        /// <summary>
        /// Performs the auto attack from the <see cref="Attacker"/> to it's <see cref="Target"/>.
        /// </summary>
        /// <param name="context">The context of the operation.</param>
        private void PerformAttack(IOperationContext context)
        {
            var rng = new Random();

            // Calculate the damage to inflict without any protections and reductions,
            // i.e. the amount of damage that the attacker can generate as it is.
            var attackPower = 1 + rng.Next(this.Attacker == null ? 10 : (int)this.Attacker.Skills[SkillType.NoWeapon].Level);

            var damageToApplyInfo = new DamageInfo(attackPower);
            var damageDoneInfo    = this.Target.ApplyDamage(damageToApplyInfo, this.Attacker?.Id ?? 0);
            var distanceOfAttack  = (this.Target.Location - (this.Attacker?.Location ?? this.Target.Location)).MaxValueIn2D;

            var packetsToSend = new List <IOutboundPacket>
            {
                new MagicEffectPacket(this.Target.Location, damageDoneInfo.Effect),
            };

            if (damageDoneInfo.Damage != 0)
            {
                TextColor damageTextColor = damageDoneInfo.Blood switch
                {
                    BloodType.Bones => TextColor.LightGrey,
                    BloodType.Fire => TextColor.Orange,
                    BloodType.Slime => TextColor.Green,
                    _ => TextColor.Red,
                };

                if (damageDoneInfo.Damage < 0)
                {
                    damageTextColor = TextColor.LightBlue;
                }

                packetsToSend.Add(new AnimatedTextPacket(this.Target.Location, damageTextColor, Math.Abs(damageDoneInfo.Damage).ToString()));
            }

            if (distanceOfAttack > 1)
            {
                // TODO: actual projectile value.
                packetsToSend.Add(new ProjectilePacket(this.Attacker.Location, this.Target.Location, ProjectileType.Bolt));
            }

            this.Target.ConsumeCredits(CombatCreditType.Defense, 1);

            this.Target.Skills[SkillType.Shield].IncreaseCounter(1);

            if (damageDoneInfo.ApplyBloodToEnvironment)
            {
                context.GameApi.CreateItemAtLocation(this.Target.Location, context.PredefinedItemSet.FindSplatterForBloodType(this.Target.BloodType));
            }

            // Normalize the attacker's defense speed based on the global round time and round that up.
            context.Scheduler.ScheduleEvent(
                new RestoreCombatCreditOperation(this.Target, CombatCreditType.Defense),
                TimeSpan.FromMilliseconds((int)Math.Round(CombatConstants.DefaultCombatRoundTimeInMs / this.Target.DefenseSpeed)));

            if (this.Attacker != null)
            {
                // this.Target.RecordDamageTaken(this.Attacker.Id, damageToApply);
                this.Attacker.ConsumeCredits(CombatCreditType.Attack, 1);

                // TODO: increase the actual skill.
                this.Attacker.Skills[SkillType.NoWeapon].IncreaseCounter(1);

                // Normalize the attacker's attack speed based on the global round time and round that up.
                context.Scheduler.ScheduleEvent(
                    new RestoreCombatCreditOperation(this.Attacker, CombatCreditType.Attack),
                    TimeSpan.FromMilliseconds((int)Math.Round(CombatConstants.DefaultCombatRoundTimeInMs / this.Attacker.AttackSpeed)));

                if (this.Attacker.Location != this.Target.Location && this.Attacker.Id != this.Target.Id)
                {
                    var directionToTarget = this.Attacker.Location.DirectionTo(this.Target.Location);
                    var turnToDirOp       = new TurnToDirectionOperation(this.Attacker, directionToTarget);

                    context.Scheduler.ScheduleEvent(turnToDirOp);
                }
            }

            this.SendNotification(context, new GenericNotification(() => context.Map.PlayersThatCanSee(this.Target.Location), packetsToSend.ToArray()));

            if (this.Target is IPlayer targetPlayer)
            {
                var squarePacket = new SquarePacket(this.Attacker.Id, SquareColor.Black);

                this.SendNotification(context, new GenericNotification(() => targetPlayer.YieldSingleItem(), squarePacket));
            }
        }
Exemple #2
0
        /// <summary>
        /// Performs the auto attack from the <see cref="Attacker"/> to it's <see cref="Target"/>.
        /// </summary>
        /// <param name="context">The context of the operation.</param>
        private void PerformAttack(IOperationContext context)
        {
            // TODO: this method has grown pretty big, it should at least be broken down when formulas get implemented.
            var rng = new Random();

            // Calculate the damage to inflict without any protections and reductions,
            // i.e. the amount of damage that the attacker can generate as it is.
            var attackPower = 1 + rng.Next(this.Attacker == null ? 10 : (int)this.Attacker.Skills[SkillType.NoWeapon].Level);

            var damageToApplyInfo = new DamageInfo(attackPower, this.Attacker);
            var damageDoneInfo    = this.Target.ApplyDamage(damageToApplyInfo, this.Attacker?.Id ?? 0);
            var distanceOfAttack  = (this.Target.Location - (this.Attacker?.Location ?? this.Target.Location)).MaxValueIn2D;

            var packetsToSendToTargetPlayer  = new List <IOutboundPacket>();
            var packetsToSendToAllSpectators = new List <IOutboundPacket>
            {
                new MagicEffectPacket(this.Target.Location, damageDoneInfo.Effect),
            };

            if (damageDoneInfo.Damage != 0)
            {
                TextColor damageTextColor = damageDoneInfo.Blood switch
                {
                    BloodType.Bones => TextColor.LightGrey,
                    BloodType.Fire => TextColor.Orange,
                    BloodType.Slime => TextColor.Green,
                    _ => TextColor.Red,
                };

                if (damageDoneInfo.Damage < 0)
                {
                    damageTextColor = TextColor.LightBlue;
                }
                else if (this.Target is IPlayer)
                {
                    var hitpointsLostMessage = $"You lose {damageDoneInfo.Damage} hitpoints";

                    if (damageDoneInfo.Dealer != null)
                    {
                        var name = string.IsNullOrWhiteSpace(damageDoneInfo.Dealer.Article) ? damageDoneInfo.Dealer.Name : $"{damageDoneInfo.Dealer.Article} {damageDoneInfo.Dealer.Name}";

                        hitpointsLostMessage += $" due to an attack by {name}";
                    }

                    hitpointsLostMessage += ".";

                    packetsToSendToTargetPlayer.Add(new TextMessagePacket(MessageType.StatusDefault, hitpointsLostMessage));
                }

                packetsToSendToAllSpectators.Add(new AnimatedTextPacket(this.Target.Location, damageTextColor, Math.Abs(damageDoneInfo.Damage).ToString()));
            }

            if (distanceOfAttack > 1)
            {
                // TODO: actual projectile value.
                packetsToSendToAllSpectators.Add(new ProjectilePacket(this.Attacker.Location, this.Target.Location, ProjectileType.Bolt));
            }

            if (this.Target.Stats[CreatureStat.DefensePoints].Decrease(1))
            {
                this.Target.Skills[SkillType.Shield].IncreaseCounter(1);

                context.Scheduler.ScheduleEvent(
                    new StatChangeOperation(this.Target, CreatureStat.DefensePoints),
                    TimeSpan.FromMilliseconds((int)Math.Round(CombatConstants.DefaultCombatRoundTimeInMs / this.Target.DefenseSpeed)));
            }

            if (damageDoneInfo.ApplyBloodToEnvironment)
            {
                context.GameApi.CreateItemAtLocation(this.Target.Location, context.PredefinedItemSet.FindSplatterForBloodType(this.Target.BloodType));
            }

            if (this.Attacker != null)
            {
                packetsToSendToTargetPlayer.Add(new SquarePacket(this.Attacker.Id, SquareColor.Black));

                if (this.Attacker.Stats[CreatureStat.AttackPoints].Decrease(1))
                {
                    // TODO: increase the actual skill.
                    this.Attacker.Skills[SkillType.NoWeapon].IncreaseCounter(1);

                    context.Scheduler.ScheduleEvent(
                        new StatChangeOperation(this.Attacker, CreatureStat.AttackPoints),
                        TimeSpan.FromMilliseconds((int)Math.Round(CombatConstants.DefaultCombatRoundTimeInMs / this.Attacker.AttackSpeed)));
                }

                if (this.Attacker.Location != this.Target.Location && this.Attacker.Id != this.Target.Id)
                {
                    var directionToTarget = this.Attacker.Location.DirectionTo(this.Target.Location);
                    var turnToDirOp       = new TurnToDirectionOperation(this.Attacker, directionToTarget);

                    context.Scheduler.ScheduleEvent(turnToDirOp);
                }

                if (this.Attacker is IPlayer attackerPlayer)
                {
                    context.GameApi.AddOrAggregateCondition(
                        attackerPlayer,
                        new InFightCondition(attackerPlayer),
                        duration: TimeSpan.FromMilliseconds(CombatConstants.DefaultInFightTimeInMs));
                }
            }

            this.SendNotification(context, new GenericNotification(() => context.Map.PlayersThatCanSee(this.Target.Location), packetsToSendToAllSpectators.ToArray()));

            if (this.Target is IPlayer targetPlayer)
            {
                this.SendNotification(context, new GenericNotification(() => targetPlayer.YieldSingleItem(), packetsToSendToTargetPlayer.ToArray()));

                context.GameApi.AddOrAggregateCondition(
                    targetPlayer,
                    new InFightCondition(targetPlayer),
                    duration: TimeSpan.FromMilliseconds(CombatConstants.DefaultInFightTimeInMs));
            }
        }