예제 #1
0
        /// <summary>
        /// Called when a player hits a target
        /// </summary>
        public override void OnDamageTarget(WorldObject target, AttackType attackType)
        {
            var attackSkill = GetCreatureSkill(GetCurrentWeaponSkill());
            var difficulty  = GetTargetEffectiveDefenseSkill(target);

            Proficiency.OnSuccessUse(this, attackSkill, difficulty);
        }
예제 #2
0
        public bool CalculateManaUsage(CastingPreCheckStatus castingPreCheckStatus, Spell spell, WorldObject target, bool isWeaponSpell, out uint manaUsed)
        {
            manaUsed = 0;
            if (castingPreCheckStatus == CastingPreCheckStatus.Success)
            {
                manaUsed = CalculateManaUsage(this, spell, target);
            }
            else if (castingPreCheckStatus == CastingPreCheckStatus.CastFailed)
            {
                manaUsed = 5;   // todo: verify with retail
            }
            var currentMana = Mana.Current;

            if (isWeaponSpell)
            {
                var caster = GetEquippedWand();
                currentMana = (uint)(caster.ItemCurMana ?? 0);
            }

            if (manaUsed > currentMana)
            {
                SendUseDoneEvent(WeenieError.YouDontHaveEnoughManaToCast);
                return(false);
            }

            Proficiency.OnSuccessUse(this, GetCreatureSkill(Skill.ManaConversion), spell.PowerMod);

            return(true);
        }
예제 #3
0
        /// <summary>
        /// Called when player successfully avoids an attack
        /// </summary>
        public override void OnEvade(WorldObject attacker, CombatType attackType)
        {
            if (UnderLifestoneProtection)
            {
                return;
            }

            // http://asheron.wikia.com/wiki/Attributes

            // Endurance will also make it less likely that you use a point of stamina to successfully evade a missile or melee attack.
            // A player is required to have Melee Defense for melee attacks or Missile Defense for missile attacks trained or specialized
            // in order for this specific ability to work. This benefit is tied to Endurance only, and it caps out at around a 75% chance
            // to avoid losing a point of stamina per successful evasion.

            var defenseSkillType = attackType == CombatType.Missile ? Skill.MissileDefense : Skill.MeleeDefense;
            var defenseSkill     = GetCreatureSkill(defenseSkillType);

            if (CombatMode != CombatMode.NonCombat)
            {
                if (defenseSkill.AdvancementClass >= SkillAdvancementClass.Trained)
                {
                    var enduranceBase = Endurance.Base;
                    // TODO: find exact formula / where it caps out at 75%
                    var enduranceCap       = 400;
                    var effective          = Math.Min(enduranceBase, enduranceCap);
                    var noStaminaUseChance = effective / enduranceCap * 0.75f;
                    if (noStaminaUseChance < ThreadSafeRandom.Next(0.0f, 1.0f))
                    {
                        UpdateVitalDelta(Stamina, -1);
                    }
                }
                else
                {
                    UpdateVitalDelta(Stamina, -1);
                }
            }
            else
            {
                UpdateVitalDelta(Stamina, -1);
            }

            Session.Network.EnqueueSend(new GameMessageSystemChat($"You evaded {attacker.Name}!", ChatMessageType.CombatEnemy));

            var creature = attacker as Creature;

            if (creature == null)
            {
                return;
            }

            var difficulty = creature.GetCreatureSkill(creature.GetCurrentWeaponSkill()).Current;

            // attackMod?
            Proficiency.OnSuccessUse(this, defenseSkill, difficulty);
        }
예제 #4
0
        /// <summary>
        /// Called when a player hits a target
        /// </summary>
        public override void OnDamageTarget(WorldObject target, CombatType attackType, bool critical)
        {
            if (critical)
            {
                target.EmoteManager.OnReceiveCritical(this);
            }

            var attackSkill = GetCreatureSkill(GetCurrentWeaponSkill());
            var difficulty  = GetTargetEffectiveDefenseSkill(target);

            Proficiency.OnSuccessUse(this, attackSkill, difficulty);
        }
예제 #5
0
        /// <summary>
        /// Performs a melee attack for the monster
        /// </summary>
        /// <returns>The length in seconds for the attack animation</returns>
        public float MeleeAttack()
        {
            var target       = AttackTarget as Creature;
            var targetPlayer = AttackTarget as Player;
            var targetPet    = AttackTarget as CombatPet;
            var combatPet    = this as CombatPet;

            if (target == null || !target.IsAlive)
            {
                FindNextTarget();
                return(0.0f);
            }

            if (CurrentMotionState.Stance == MotionStance.NonCombat)
            {
                DoAttackStance();
            }

            // choose a random combat maneuver
            var maneuver = GetCombatManeuver();

            if (maneuver == null)
            {
                Console.WriteLine($"Combat maneuver null! Stance {CurrentMotionState.Stance}, MotionTable {MotionTableId:X8}");
                return(0.0f);
            }

            AttackHeight = maneuver.AttackHeight;

            DoSwingMotion(AttackTarget, maneuver, out float animLength, out var attackFrames);
            PhysicsObj.stick_to_object(AttackTarget.PhysicsObj.ID);

            var numStrikes = attackFrames.Count;

            var actionChain = new ActionChain();

            var prevTime = 0.0f;

            for (var i = 0; i < numStrikes; i++)
            {
                actionChain.AddDelaySeconds(attackFrames[i] * animLength - prevTime);
                prevTime = attackFrames[i] * animLength;

                actionChain.AddAction(this, () =>
                {
                    if (AttackTarget == null || IsDead)
                    {
                        return;
                    }

                    if (WeenieType == WeenieType.GamePiece)
                    {
                        target.TakeDamage(this, DamageType.Slash, target.Health.Current);
                        (this as GamePiece).OnDealtDamage();
                        return;
                    }

                    var weapon      = GetEquippedWeapon();
                    var damageEvent = DamageEvent.CalculateDamage(this, target, weapon, maneuver);

                    //var damage = CalculateDamage(ref damageType, maneuver, bodyPart, ref critical, ref shieldMod);

                    if (damageEvent.HasDamage)
                    {
                        if (combatPet != null || targetPet != null)
                        {
                            // combat pet inflicting or receiving damage
                            //Console.WriteLine($"{target.Name} taking {Math.Round(damage)} {damageType} damage from {Name}");
                            target.TakeDamage(this, damageEvent.DamageType, damageEvent.Damage);
                            EmitSplatter(target, damageEvent.Damage);
                        }
                        else if (targetPlayer != null)
                        {
                            // this is a player taking damage
                            targetPlayer.TakeDamage(this, damageEvent);

                            if (damageEvent.ShieldMod != 1.0f)
                            {
                                var shieldSkill = targetPlayer.GetCreatureSkill(Skill.Shield);
                                Proficiency.OnSuccessUse(targetPlayer, shieldSkill, shieldSkill.Current); // ?
                            }
                        }
                    }
                    else
                    {
                        target.OnEvade(this, CombatType.Melee);
                    }

                    if (combatPet != null)
                    {
                        combatPet.PetOnAttackMonster(target);
                    }
                });
            }
            actionChain.EnqueueChain();

            // TODO: figure out exact speed / delay formula
            var meleeDelay = ThreadSafeRandom.Next(MeleeDelayMin, MeleeDelayMax);

            NextAttackTime = Timers.RunningTime + animLength + meleeDelay;
            return(animLength);
        }
예제 #6
0
        public override void OnCollideObject(WorldObject _target)
        {
            //Console.WriteLine($"{Name}.OnCollideObject({_target.Name})");

            var player = ProjectileSource as Player;

            if (player != null)
            {
                player.LastHitSpellProjectile = Spell;
            }

            // ensure valid creature target
            // non-target objects will be excluded beforehand from collision detection
            var target = _target as Creature;

            if (target == null || player == target)
            {
                OnCollideEnvironment();
                return;
            }

            ProjectileImpact();

            // for untargeted multi-projectile war spells launched by monsters,
            // ensure monster can damage target
            var sourceCreature = ProjectileSource as Creature;

            if (sourceCreature != null && !sourceCreature.CanDamage(target))
            {
                return;
            }

            // if player target, ensure matching PK status
            var targetPlayer = target as Player;

            var pkError = CheckPKStatusVsTarget(player, targetPlayer, Spell);

            if (pkError != null)
            {
                if (player != null)
                {
                    player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, pkError[0], target.Name));
                }

                if (targetPlayer != null)
                {
                    targetPlayer.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(targetPlayer.Session, pkError[1], ProjectileSource.Name));
                }

                return;
            }

            var critical = false;
            var damage   = CalculateDamage(ProjectileSource, target, ref critical);

            // null damage -> target resisted; damage of -1 -> target already dead
            if (damage != null && damage != -1)
            {
                // handle void magic DoTs:
                // instead of instant damage, add DoT to target's enchantment registry
                if (Spell.School == MagicSchool.VoidMagic && Spell.Duration > 0)
                {
                    var dot = ProjectileSource.CreateEnchantment(target, ProjectileSource, Spell);
                    if (dot.Message != null && player != null)
                    {
                        player.Session.Network.EnqueueSend(dot.Message);
                    }

                    // corruption / corrosion playscript?
                    //target.EnqueueBroadcast(new GameMessageScript(target.Guid, ACE.Entity.Enum.PlayScript.HealthDownVoid));
                    //target.EnqueueBroadcast(new GameMessageScript(target.Guid, ACE.Entity.Enum.PlayScript.DirtyFightingDefenseDebuff));
                }
                else
                {
                    DamageTarget(target, damage, critical);
                }

                if (player != null)
                {
                    Proficiency.OnSuccessUse(player, player.GetCreatureSkill(Spell.School), Spell.PowerMod);
                }

                // handle target procs
                // note that for untargeted multi-projectile spells,
                // ProjectileTarget will be null here, so procs will not apply
                if (sourceCreature != null && ProjectileTarget != null)
                {
                    sourceCreature.TryProcEquippedItems(target, false);
                }
            }

            // also called on resist
            if (player != null && targetPlayer == null)
            {
                player.OnAttackMonster(target);
            }
        }
예제 #7
0
        public static void UseUnlocker(Player player, WorldObject unlocker, WorldObject target)
        {
            ActionChain chain = new ActionChain();

            chain.AddAction(player, () =>
            {
                if (unlocker.WeenieType == WeenieType.Lockpick &&
                    player.Skills[Skill.Lockpick].AdvancementClass != SkillAdvancementClass.Trained &&
                    player.Skills[Skill.Lockpick].AdvancementClass != SkillAdvancementClass.Specialized)
                {
                    player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouArentTrainedInLockpicking));
                    return;
                }
                if (target is Lock @lock)
                {
                    UnlockResults result = UnlockResults.IncorrectKey;
                    var difficulty       = 0;
                    if (unlocker.WeenieType == WeenieType.Lockpick)
                    {
                        result = @lock.Unlock(player.Skills[Skill.Lockpick].Current, ref difficulty);
                    }
                    else if (unlocker is Key woKey)
                    {
                        if (target is Door woDoor)
                        {
                            if (woDoor.LockCode == "") // the door isn't to be opened with keys
                            {
                                player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat));
                                return;
                            }
                        }
                        result = @lock.Unlock(woKey.KeyCode);
                    }

                    switch (result)
                    {
                    case UnlockResults.UnlockSuccess:

                        if (unlocker.WeenieType == WeenieType.Lockpick)
                        {
                            player.HandleActionApplySoundEffect(Sound.Lockpicking);    // Sound.Lockpicking doesn't work via EnqueueBroadcastSound for some reason.
                        }
                        player.Session.Network.EnqueueSend(new GameMessageSystemChat($"You have successfully picked the lock! It is now unlocked.", ChatMessageType.Craft));

                        var lockpickSkill = player.GetCreatureSkill(Skill.Lockpick);
                        Proficiency.OnSuccessUse(player, lockpickSkill, (uint)difficulty);

                        ConsumeUnlocker(player, unlocker);
                        break;

                    case UnlockResults.Open:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockWhatIsOpen));
                        break;

                    case UnlockResults.AlreadyUnlocked:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.LockAlreadyUnlocked));
                        break;

                    case UnlockResults.PickLockFailed:
                        target.EnqueueBroadcast(new GameMessageSound(target.Guid, Sound.PicklockFail, 1.0f));
                        ConsumeUnlocker(player, unlocker);
                        break;

                    case UnlockResults.CannotBePicked:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat));
                        break;

                    case UnlockResults.IncorrectKey:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.KeyDoesntFitThisLock));
                        break;
                    }
                }
                else
                {
                    player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat));
                }
            });

            chain.EnqueueChain();
        }
예제 #8
0
        /// <summary>
        /// Method used for handling player targeted spell casts
        /// </summary>
        public void CreatePlayerSpell(WorldObject target, TargetCategory targetCategory, uint spellId)
        {
            var player         = this;
            var creatureTarget = target as Creature;

            if (player.IsBusy == true)
            {
                player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YoureTooBusy));
                return;
            }
            player.IsBusy = true;

            var spell = new Spell(spellId);

            if (spell.NotFound)
            {
                if (spell._spellBase == null)
                {
                    Session.Network.EnqueueSend(new GameEventCommunicationTransientString(Session, $"SpellId {spellId} Invalid."));
                    Session.Network.EnqueueSend(new GameEventUseDone(Session, WeenieError.None));
                }
                else
                {
                    Session.Network.EnqueueSend(new GameMessageSystemChat($"{spell.Name} spell not implemented, yet!", ChatMessageType.System));
                    Session.Network.EnqueueSend(new GameEventUseDone(Session, WeenieError.MagicInvalidSpellType));
                }

                player.IsBusy = false;
                return;
            }

            if (IsInvalidTarget(spell, target))
            {
                player.Session.Network.EnqueueSend(new GameEventCommunicationTransientString(player.Session, $"{spell.Name} cannot be cast on {target.Name}."));
                player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.None));
                player.IsBusy = false;
                return;
            }

            // if casting implement has spell built in,
            // use spellcraft from the item, instead of player's magic skill?
            var caster        = GetEquippedWand();
            var isWeaponSpell = IsWeaponSpell(spell);

            // Grab player's skill level in the spell's Magic School
            var magicSkill = player.GetCreatureSkill(spell.School).Current;

            if (isWeaponSpell && caster.ItemSpellcraft != null)
            {
                magicSkill = (uint)caster.ItemSpellcraft;
            }

            if (targetCategory == TargetCategory.WorldObject)
            {
                if (target.Guid != Guid)
                {
                    var targetLoc = target;
                    if (targetLoc.WielderId != null)
                    {
                        targetLoc = CurrentLandblock?.GetObject(targetLoc.WielderId.Value);
                    }

                    float distanceTo = Location.Distance2D(targetLoc.Location);

                    if (distanceTo > spell.BaseRangeConstant + magicSkill * spell.BaseRangeMod)
                    {
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.None),
                                                           new GameMessageSystemChat($"Target is out of range!", ChatMessageType.Magic));
                        player.IsBusy = false;
                        return;
                    }
                }
            }

            var difficulty = spell.Power;

            // is this needed? should talismans remain the same, regardless of player spell formula?
            spell.Formula.GetPlayerFormula(player);

            var castingPreCheckStatus = CastingPreCheckStatus.CastFailed;

            if (magicSkill > 0 && magicSkill >= (int)difficulty - 50)
            {
                var chance = 1.0f - SkillCheck.GetMagicSkillChance((int)magicSkill, (int)difficulty);
                var rng    = ThreadSafeRandom.Next(0.0f, 1.0f);
                if (chance < rng || isWeaponSpell)
                {
                    castingPreCheckStatus = CastingPreCheckStatus.Success;
                }
            }

            // limit casting time between war and void
            if (spell.School == MagicSchool.VoidMagic && LastSuccessCast_School == MagicSchool.WarMagic ||
                spell.School == MagicSchool.WarMagic && LastSuccessCast_School == MagicSchool.VoidMagic)
            {
                // roll each time?
                var timeLimit = ThreadSafeRandom.Next(3.0f, 5.0f);

                if (Time.GetUnixTime() - LastSuccessCast_Time < timeLimit)
                {
                    var curType  = spell.School == MagicSchool.WarMagic ? "War" : "Void";
                    var prevType = LastSuccessCast_School == MagicSchool.VoidMagic ? "Nether" : "Elemental";

                    Session.Network.EnqueueSend(new GameMessageSystemChat($"The {prevType} energies permeating your blood cause this {curType} magic to fail.", ChatMessageType.Magic));

                    castingPreCheckStatus = CastingPreCheckStatus.CastFailed;
                }
            }

            // Calculate mana usage
            uint manaUsed = CalculateManaUsage(player, spell, target);

            var currentMana = player.Mana.Current;

            if (isWeaponSpell)
            {
                currentMana = (uint)(caster.ItemCurMana ?? 0);
            }

            if (manaUsed > currentMana)
            {
                player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouDontHaveEnoughManaToCast));
                IsBusy = false; // delay?
                return;
            }

            // begin spellcasting
            Proficiency.OnSuccessUse(player, player.GetCreatureSkill(Skill.ManaConversion), spell.PowerMod);

            if (!isWeaponSpell)
            {
                player.UpdateVitalDelta(player.Mana, -(int)manaUsed);
            }
            else
            {
                caster.ItemCurMana -= (int)manaUsed;
            }

            spell.Formula.GetPlayerFormula(player);

            string spellWords = spell._spellBase.GetSpellWords(DatManager.PortalDat.SpellComponentsTable);

            if (spellWords != null && !isWeaponSpell)
            {
                EnqueueBroadcast(new GameMessageCreatureMessage(spellWords, Name, Guid.Full, ChatMessageType.Spellcasting));
            }

            var spellChain = new ActionChain();
            var castSpeed  = 2.0f;  // hardcoded for player spell casting?

            var startPos = new Position(Location);

            // do wind-up gestures: fastcast has no windup (creature enchantments)
            if (!spell.Flags.HasFlag(SpellFlags.FastCast) && !isWeaponSpell)
            {
                // note that ACE is currently sending the windup motion and the casting gesture
                // at the same time. the client is automatically queueing these animations to run at the correct time.

                foreach (var windupGesture in spell.Formula.WindupGestures)
                {
                    spellChain.AddAction(this, () =>
                    {
                        var motionWindUp = new Motion(MotionStance.Magic, windupGesture, castSpeed);
                        EnqueueBroadcastMotion(motionWindUp);
                    });
                }
            }

            // cast spell
            spellChain.AddAction(this, () =>
            {
                var castGesture = spell.Formula.CastGesture;
                if (isWeaponSpell && caster.UseUserAnimation != 0)
                {
                    castGesture = caster.UseUserAnimation;
                }

                var motionCastSpell = new Motion(MotionStance.Magic, castGesture, castSpeed);
                EnqueueBroadcastMotion(motionCastSpell);
            });

            var castingDelay = spell.Formula.GetCastTime(MotionTableId, castSpeed, isWeaponSpell);

            spellChain.AddDelaySeconds(castingDelay);

            bool movedTooFar = false;

            spellChain.AddAction(this, () =>
            {
                if (!isWeaponSpell)
                {
                    TryBurnComponents(spell);
                }

                // check windup move distance cap
                var endPos = new Position(Location);
                var dist   = startPos.DistanceTo(endPos);

                if (dist > Windup_MaxMove)
                {
                    castingPreCheckStatus = CastingPreCheckStatus.CastFailed;
                    movedTooFar           = true;
                }

                var pk_error = CheckPKStatusVsTarget(player, target, spell);
                if (pk_error != null)
                {
                    castingPreCheckStatus = CastingPreCheckStatus.InvalidPKStatus;
                }

                var useDone = WeenieError.None;

                switch (castingPreCheckStatus)
                {
                case CastingPreCheckStatus.Success:

                    if ((spell.Flags & SpellFlags.FellowshipSpell) == 0)
                    {
                        CreatePlayerSpell(target, spell);
                    }
                    else
                    {
                        var fellows = GetFellowshipTargets();
                        foreach (var fellow in fellows)
                        {
                            CreatePlayerSpell(fellow, spell);
                        }
                    }
                    break;

                case CastingPreCheckStatus.InvalidPKStatus:

                    if (spell.NumProjectiles > 0)
                    {
                        switch (spell.School)
                        {
                        case MagicSchool.WarMagic:
                            WarMagic(target, spell);
                            break;

                        case MagicSchool.VoidMagic:
                            VoidMagic(target, spell);
                            break;

                        case MagicSchool.LifeMagic:
                            LifeMagic(target, spell, out uint damage, out bool critical, out var enchantmentStatus);
                            break;
                        }
예제 #9
0
        public override void OnCollideObject(WorldObject _target)
        {
            //Console.WriteLine($"{Name}.OnCollideObject({_target.Name})");

            var player = ProjectileSource as Player;

            if (player != null)
            {
                player.LastHitSpellProjectile = Spell;
            }

            // ensure valid creature target
            // non-target objects will be excluded beforehand from collision detection
            var target = _target as Creature;

            if (target == null)
            {
                OnCollideEnvironment();
                return;
            }

            ProjectileImpact();

            // if player target, ensure matching PK status
            var targetPlayer = target as Player;

            var checkPKStatusVsTarget = CheckPKStatusVsTarget(player, targetPlayer, Spell);

            if (checkPKStatusVsTarget != null && checkPKStatusVsTarget == false)
            {
                player.Session.Network.EnqueueSend(new GameEventWeenieError(player.Session, WeenieError.InvalidPkStatus));
                return;
            }

            var critical = false;
            var damage   = CalculateDamage(ProjectileSource, target, ref critical);

            // null damage -> target resisted; damage of -1 -> target already dead
            if (damage != null && damage != -1)
            {
                // handle void magic DoTs:
                // instead of instant damage, add DoT to target's enchantment registry
                if (Spell.School == MagicSchool.VoidMagic && Spell.Duration > 0)
                {
                    var dot = ProjectileSource.CreateEnchantment(target, ProjectileSource, Spell);
                    if (dot.message != null && player != null)
                    {
                        player.Session.Network.EnqueueSend(dot.message);
                    }

                    // corruption / corrosion playscript?
                    //target.EnqueueBroadcast(new GameMessageScript(target.Guid, ACE.Entity.Enum.PlayScript.HealthDownVoid));
                    //target.EnqueueBroadcast(new GameMessageScript(target.Guid, ACE.Entity.Enum.PlayScript.DirtyFightingDefenseDebuff));
                }
                else
                {
                    DamageTarget(target, damage, critical);
                }

                if (player != null)
                {
                    Proficiency.OnSuccessUse(player, player.GetCreatureSkill(Spell.School), Spell.PowerMod);
                }
            }

            // also called on resist
            if (player != null && targetPlayer == null)
            {
                player.OnAttackMonster(target);
            }
        }
예제 #10
0
        public override void OnCollideObject(WorldObject target)
        {
            //Console.WriteLine($"{Name}.OnCollideObject({target.Name})");

            var player = ProjectileSource as Player;

            if (Info != null && player != null && player.DebugSpell)
            {
                player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{Name}.OnCollideObject({target?.Name} ({target?.Guid}))", ChatMessageType.Broadcast));
                player.Session.Network.EnqueueSend(new GameMessageSystemChat(Info.ToString(), ChatMessageType.Broadcast));
            }

            ProjectileImpact();

            // ensure valid creature target
            var creatureTarget = target as Creature;

            if (creatureTarget == null || target == ProjectileSource)
            {
                return;
            }

            if (player != null)
            {
                player.LastHitSpellProjectile = Spell;
            }

            // ensure caster can damage target
            var sourceCreature = ProjectileSource as Creature;

            if (sourceCreature != null && !sourceCreature.CanDamage(creatureTarget))
            {
                return;
            }

            // if player target, ensure matching PK status
            var targetPlayer = creatureTarget as Player;

            var pkError = ProjectileSource?.CheckPKStatusVsTarget(creatureTarget, Spell);

            if (pkError != null)
            {
                if (player != null)
                {
                    player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, pkError[0], creatureTarget.Name));
                }

                if (targetPlayer != null)
                {
                    targetPlayer.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(targetPlayer.Session, pkError[1], ProjectileSource.Name));
                }

                return;
            }

            var critical     = false;
            var critDefended = false;
            var overpower    = false;

            var damage = CalculateDamage(ProjectileSource, Caster, creatureTarget, ref critical, ref critDefended, ref overpower);

            if (damage != null)
            {
                // handle void magic DoTs:
                // instead of instant damage, add DoT to target's enchantment registry
                if (Spell.School == MagicSchool.VoidMagic && Spell.Duration > 0)
                {
                    var dot = ProjectileSource.CreateEnchantment(creatureTarget, ProjectileSource, Spell);
                    if (dot.Message != null && player != null)
                    {
                        player.Session.Network.EnqueueSend(dot.Message);
                    }

                    // corruption / corrosion playscript?
                    //target.EnqueueBroadcast(new GameMessageScript(target.Guid, PlayScript.HealthDownVoid));
                    //target.EnqueueBroadcast(new GameMessageScript(target.Guid, PlayScript.DirtyFightingDefenseDebuff));
                }
                else
                {
                    DamageTarget(creatureTarget, damage.Value, critical, critDefended, overpower);
                }

                if (player != null)
                {
                    Proficiency.OnSuccessUse(player, player.GetCreatureSkill(Spell.School), Spell.PowerMod);
                }

                // handle target procs
                // note that for untargeted multi-projectile spells,
                // ProjectileTarget will be null here, so procs will not apply
                if (sourceCreature != null && ProjectileTarget != null)
                {
                    // TODO figure out why cross-landblock group operations are happening here. We shouldn't need this code Mag-nus 2021-02-09
                    bool threadSafe = true;

                    if (LandblockManager.CurrentlyTickingLandblockGroupsMultiThreaded)
                    {
                        // Ok... if we got here, we're likely in the parallel landblock physics processing.
                        if (sourceCreature.CurrentLandblock == null || creatureTarget.CurrentLandblock == null || sourceCreature.CurrentLandblock.CurrentLandblockGroup != creatureTarget.CurrentLandblock.CurrentLandblockGroup)
                        {
                            threadSafe = false;
                        }
                    }

                    if (threadSafe)
                    {
                        // This can result in spell projectiles being added to either sourceCreature or creatureTargets landblock.
                        sourceCreature.TryProcEquippedItems(creatureTarget, false);
                    }
                    else
                    {
                        // sourceCreature and creatureTarget are now in different landblock groups.
                        // What has likely happened is that sourceCreature sent a projectile toward creatureTarget. Before impact, sourceCreature was teleported away.
                        // To perform this fully thread safe, we would enqueue the work onto worldManager.
                        // WorldManager.EnqueueAction(new ActionEventDelegate(() => sourceCreature.TryProcEquippedItems(creatureTarget, false)));
                        // But, to keep it simple, we will just ignore it and not bother with TryProcEquippedItems for this particular impact.
                    }
                }
            }

            // also called on resist
            if (player != null && targetPlayer == null)
            {
                player.OnAttackMonster(creatureTarget);
            }

            if (player == null && targetPlayer == null)
            {
                // check for faction combat
                if (sourceCreature != null && creatureTarget != null && (sourceCreature.AllowFactionCombat(creatureTarget) || sourceCreature.PotentialFoe(creatureTarget)))
                {
                    sourceCreature.MonsterOnAttackMonster(creatureTarget);
                }
            }
        }
예제 #11
0
        public static void OnCollideObject(WorldObject worldObject, WorldObject target)
        {
            if (!worldObject.PhysicsObj.is_active())
            {
                return;
            }

            //Console.WriteLine($"Projectile.OnCollideObject - {WorldObject.Name} ({WorldObject.Guid}) -> {target.Name} ({target.Guid})");

            if (worldObject.ProjectileTarget == null || worldObject.ProjectileTarget != target)
            {
                //Console.WriteLine("Unintended projectile target! (should be " + ProjectileTarget.Guid.Full.ToString("X8") + " - " + ProjectileTarget.Name + ")");
                OnCollideEnvironment(worldObject);
                return;
            }

            // take damage
            var sourceCreature = worldObject.ProjectileSource as Creature;
            var sourcePlayer   = worldObject.ProjectileSource as Player;
            var targetCreature = target as Creature;

            DamageEvent damageEvent = null;

            if (targetCreature != null)
            {
                if (sourcePlayer != null)
                {
                    // player damage monster or player
                    damageEvent = sourcePlayer.DamageTarget(targetCreature, worldObject);

                    if (damageEvent != null && damageEvent.HasDamage)
                    {
                        worldObject.EnqueueBroadcast(new GameMessageSound(worldObject.Guid, Sound.Collision, 1.0f));
                    }
                }
                else if (sourceCreature != null && sourceCreature.AttackTarget != null)
                {
                    var targetPlayer = sourceCreature.AttackTarget as Player;

                    damageEvent = DamageEvent.CalculateDamage(sourceCreature, targetCreature, worldObject);

                    if (targetPlayer != null)
                    {
                        // monster damage player
                        if (damageEvent.HasDamage)
                        {
                            targetPlayer.TakeDamage(sourceCreature, damageEvent);

                            // blood splatter?

                            if (damageEvent.ShieldMod != 1.0f)
                            {
                                var shieldSkill = targetPlayer.GetCreatureSkill(Skill.Shield);
                                Proficiency.OnSuccessUse(targetPlayer, shieldSkill, shieldSkill.Current);   // ??
                            }
                        }
                        else
                        {
                            targetPlayer.OnEvade(sourceCreature, CombatType.Missile);
                        }
                    }
                    else
                    {
                        // monster damage pet
                        if (damageEvent.HasDamage)
                        {
                            targetCreature.TakeDamage(sourceCreature, damageEvent.DamageType, damageEvent.Damage);

                            // blood splatter?
                        }

                        if (!(targetCreature is CombatPet))
                        {
                            // faction mobs and foetype
                            sourceCreature.MonsterOnAttackMonster(targetCreature);
                        }
                    }
                }

                // handle target procs
                if (damageEvent != null && damageEvent.HasDamage)
                {
                    // Ok... if we got here, we're likely in the parallel landblock physics processing.
                    // We're currently on the thread for worldObject, but we're wanting to perform some work on sourceCreature which can result in a new spell being created
                    // and added to the sourceCreature's current landblock, which, could be on a separate thread.
                    // Any chance of a cross landblock group work (and thus cross thread), should be enqueued onto the target object to maintain thread safety.
                    if (sourceCreature.CurrentLandblock == null || sourceCreature.CurrentLandblock == worldObject.CurrentLandblock)
                    {
                        sourceCreature.TryProcEquippedItems(targetCreature, false);
                    }
                    else
                    {
                        sourceCreature.EnqueueAction(new ActionEventDelegate(() => sourceCreature.TryProcEquippedItems(targetCreature, false)));
                    }
                }
            }

            worldObject.CurrentLandblock?.RemoveWorldObject(worldObject.Guid, showError: !worldObject.PhysicsObj.entering_world);
            worldObject.PhysicsObj.set_active(false);

            worldObject.HitMsg = true;
        }
예제 #12
0
        public static void OnCollideObject(WorldObject worldObject, WorldObject target)
        {
            if (!worldObject.PhysicsObj.is_active())
            {
                return;
            }

            //Console.WriteLine($"Projectile.OnCollideObject - {WorldObject.Name} ({WorldObject.Guid}) -> {target.Name} ({target.Guid})");

            if (worldObject.ProjectileTarget == null || worldObject.ProjectileTarget != target)
            {
                //Console.WriteLine("Unintended projectile target! (should be " + ProjectileTarget.Guid.Full.ToString("X8") + " - " + ProjectileTarget.Name + ")");
                OnCollideEnvironment(worldObject);
                return;
            }

            // take damage
            var sourceCreature = worldObject.ProjectileSource as Creature;
            var sourcePlayer   = worldObject.ProjectileSource as Player;
            var targetCreature = target as Creature;

            DamageEvent damageEvent = null;

            if (targetCreature != null)
            {
                if (sourcePlayer != null)
                {
                    // player damage monster or player
                    damageEvent = sourcePlayer.DamageTarget(targetCreature, worldObject);

                    if (damageEvent != null && damageEvent.HasDamage)
                    {
                        worldObject.EnqueueBroadcast(new GameMessageSound(worldObject.Guid, Sound.Collision, 1.0f));
                    }
                }
                else if (sourceCreature != null && sourceCreature.AttackTarget != null)
                {
                    // todo: clean this up
                    var targetPlayer = sourceCreature.AttackTarget as Player;

                    damageEvent = DamageEvent.CalculateDamage(sourceCreature, targetCreature, worldObject);

                    if (targetPlayer != null)
                    {
                        // monster damage player
                        if (damageEvent.HasDamage)
                        {
                            targetPlayer.TakeDamage(sourceCreature, damageEvent);

                            // blood splatter?

                            if (damageEvent.ShieldMod != 1.0f)
                            {
                                var shieldSkill = targetPlayer.GetCreatureSkill(Skill.Shield);
                                Proficiency.OnSuccessUse(targetPlayer, shieldSkill, shieldSkill.Current);   // ??
                            }

                            // handle Dirty Fighting
                            if (sourceCreature.GetCreatureSkill(Skill.DirtyFighting).AdvancementClass >= SkillAdvancementClass.Trained)
                            {
                                sourceCreature.FightDirty(targetPlayer, damageEvent.Weapon);
                            }
                        }
                        else
                        {
                            targetPlayer.OnEvade(sourceCreature, CombatType.Missile);
                        }
                    }
                    else
                    {
                        // monster damage pet
                        if (damageEvent.HasDamage)
                        {
                            targetCreature.TakeDamage(sourceCreature, damageEvent.DamageType, damageEvent.Damage);

                            // blood splatter?

                            // handle Dirty Fighting
                            if (sourceCreature.GetCreatureSkill(Skill.DirtyFighting).AdvancementClass >= SkillAdvancementClass.Trained)
                            {
                                sourceCreature.FightDirty(targetCreature, damageEvent.Weapon);
                            }
                        }

                        if (!(targetCreature is CombatPet))
                        {
                            // faction mobs and foetype
                            sourceCreature.MonsterOnAttackMonster(targetCreature);
                        }
                    }
                }

                // handle target procs
                if (damageEvent != null && damageEvent.HasDamage)
                {
                    bool threadSafe = true;

                    if (LandblockManager.CurrentlyTickingLandblockGroupsMultiThreaded)
                    {
                        // Ok... if we got here, we're likely in the parallel landblock physics processing.
                        if (worldObject.CurrentLandblock == null || sourceCreature.CurrentLandblock == null || targetCreature.CurrentLandblock == null || worldObject.CurrentLandblock.CurrentLandblockGroup != sourceCreature.CurrentLandblock.CurrentLandblockGroup || sourceCreature.CurrentLandblock.CurrentLandblockGroup != targetCreature.CurrentLandblock.CurrentLandblockGroup)
                        {
                            threadSafe = false;
                        }
                    }

                    if (threadSafe)
                    {
                        // This can result in spell projectiles being added to either sourceCreature or targetCreature landblock.
                        // worldObject is hitting targetCreature, so they should almost always be in the same landblock
                        worldObject.TryProcEquippedItems(sourceCreature, targetCreature, false, worldObject.ProjectileLauncher);
                    }
                    else
                    {
                        // sourceCreature and creatureTarget are now in different landblock groups.
                        // What has likely happened is that sourceCreature sent a projectile toward creatureTarget. Before impact, sourceCreature was teleported away.
                        // To perform this fully thread safe, we would enqueue the work onto worldManager.
                        // WorldManager.EnqueueAction(new ActionEventDelegate(() => sourceCreature.TryProcEquippedItems(targetCreature, false)));
                        // But, to keep it simple, we will just ignore it and not bother with TryProcEquippedItems for this particular impact.
                    }
                }
            }

            worldObject.CurrentLandblock?.RemoveWorldObject(worldObject.Guid, showError: !worldObject.PhysicsObj.entering_world);
            worldObject.PhysicsObj.set_active(false);

            worldObject.HitMsg = true;
        }
        public static void OnCollideObject(WorldObject worldObject, WorldObject target)
        {
            if (!worldObject.PhysicsObj.is_active())
            {
                return;
            }

            //Console.WriteLine($"Projectile.OnCollideObject - {WorldObject.Name} ({WorldObject.Guid}) -> {target.Name} ({target.Guid})");

            if (worldObject.ProjectileTarget == null || worldObject.ProjectileTarget != target)
            {
                //Console.WriteLine("Unintended projectile target! (should be " + ProjectileTarget.Guid.Full.ToString("X8") + " - " + ProjectileTarget.Name + ")");
                OnCollideEnvironment(worldObject);
                return;
            }

            // take damage
            var sourceCreature = worldObject.ProjectileSource as Creature;
            var sourcePlayer   = worldObject.ProjectileSource as Player;
            var targetCreature = target as Creature;

            DamageEvent damageEvent = null;

            if (targetCreature != null)
            {
                if (sourcePlayer != null)
                {
                    var weapon = sourcePlayer.GetEquippedMissileWeapon();

                    if (weapon != null && weapon.NumTimesTinkered > 0)
                    {
                        var maxdmg = 0;

                        if (weapon.W_WeaponType == WeaponType.Bow)
                        {
                            maxdmg = (int)PropertyManager.GetDouble("bow_damage").Item;
                        }
                        else if (weapon.W_WeaponType == WeaponType.Crossbow)
                        {
                            maxdmg += (int)PropertyManager.GetDouble("xbow_damage").Item;
                        }
                        else if (weapon.W_WeaponType == WeaponType.Thrown)
                        {
                            maxdmg += (int)PropertyManager.GetDouble("thrown_damage").Item;
                        }
                        maxdmg *= weapon.NumTimesTinkered;
                        var dmgrng = maxdmg / 2;
                        worldObject.Damage += dmgrng;
                    }

                    // player damage monster or player
                    damageEvent = sourcePlayer.DamageTarget(targetCreature, worldObject);

                    var targetPlayer = targetCreature as Player;
                    if (targetPlayer != null)
                    {
                        if (damageEvent != null && damageEvent.HasDamage)
                        {
                            var damageCap = PropertyManager.GetLong("pvp_damage_cap").Item;
                            if (damageEvent.Damage > damageCap)
                            {
                                damageEvent.Damage = damageCap;
                            }
                        }
                    }

                    if (damageEvent != null && damageEvent.HasDamage)
                    {
                        worldObject.EnqueueBroadcast(new GameMessageSound(worldObject.Guid, Sound.Collision, 1.0f));
                    }
                }
                else if (sourceCreature != null && sourceCreature.AttackTarget != null)
                {
                    var targetPlayer = sourceCreature.AttackTarget as Player;

                    damageEvent = DamageEvent.CalculateDamage(sourceCreature, targetCreature, worldObject);

                    if (targetPlayer != null)
                    {
                        // monster damage player
                        if (damageEvent.HasDamage)
                        {
                            targetPlayer.TakeDamage(sourceCreature, damageEvent);

                            // blood splatter?

                            if (damageEvent.ShieldMod != 1.0f)
                            {
                                var shieldSkill = targetPlayer.GetCreatureSkill(Skill.Shield);
                                Proficiency.OnSuccessUse(targetPlayer, shieldSkill, shieldSkill.Current);   // ??
                            }
                        }
                        else
                        {
                            targetPlayer.OnEvade(sourceCreature, CombatType.Missile);
                        }
                    }
                    else
                    {
                        // monster damage pet
                        if (damageEvent.HasDamage)
                        {
                            targetCreature.TakeDamage(sourceCreature, damageEvent.DamageType, damageEvent.Damage);

                            // blood splatter?
                        }

                        if (!(targetCreature is CombatPet))
                        {
                            // faction mobs and foetype
                            sourceCreature.MonsterOnAttackMonster(targetCreature);
                        }
                    }
                }

                // handle target procs
                if (damageEvent != null && damageEvent.HasDamage)
                {
                    bool threadSafe = true;

                    if (LandblockManager.CurrentlyTickingLandblockGroupsMultiThreaded)
                    {
                        // Ok... if we got here, we're likely in the parallel landblock physics processing.
                        if (sourceCreature.CurrentLandblock == null || targetCreature.CurrentLandblock == null || sourceCreature.CurrentLandblock.CurrentLandblockGroup != targetCreature.CurrentLandblock.CurrentLandblockGroup)
                        {
                            threadSafe = false;
                        }
                    }

                    if (threadSafe)
                    {
                        // This can result in spell projectiles being added to either sourceCreature or targetCreature landblock.
                        sourceCreature.TryProcEquippedItems(targetCreature, false);
                    }
                    else
                    {
                        // sourceCreature and creatureTarget are now in different landblock groups.
                        // What has likely happened is that sourceCreature sent a projectile toward creatureTarget. Before impact, sourceCreature was teleported away.
                        // To perform this fully thread safe, we would enqueue the work onto worldManager.
                        // WorldManager.EnqueueAction(new ActionEventDelegate(() => sourceCreature.TryProcEquippedItems(targetCreature, false)));
                        // But, to keep it simple, we will just ignore it and not bother with TryProcEquippedItems for this particular impact.
                    }
                }
            }

            worldObject.CurrentLandblock?.RemoveWorldObject(worldObject.Guid, showError: !worldObject.PhysicsObj.entering_world);
            worldObject.PhysicsObj.set_active(false);

            worldObject.HitMsg = true;
        }
예제 #14
0
        private void CreatePlayerSpell(WorldObject target, Spell spell)
        {
            var targetCreature = target as Creature;
            var targetPlayer   = target as Player;

            bool targetDeath;
            var  enchantmentStatus = new EnchantmentStatus(spell);

            LastSuccessCast_School = spell.School;
            LastSuccessCast_Time   = Time.GetUnixTime();

            switch (spell.School)
            {
            case MagicSchool.WarMagic:
                WarMagic(target, spell);

                break;

            case MagicSchool.VoidMagic:
                VoidMagic(target, spell);
                break;

            case MagicSchool.CreatureEnchantment:

                if (targetPlayer == null)
                {
                    OnAttackMonster(targetCreature);
                }

                if (spell.IsHarmful)
                {
                    var resisted = ResistSpell(target, spell);
                    if (resisted == true)
                    {
                        break;
                    }
                    if (resisted == null)
                    {
                        log.Error("Something went wrong with the Magic resistance check");
                        break;
                    }
                }

                if (targetCreature != null && targetCreature.NonProjectileMagicImmune)
                {
                    Session.Network.EnqueueSend(new GameMessageSystemChat($"You fail to affect {targetCreature.Name} with {spell.Name}", ChatMessageType.Magic));
                    break;
                }

                EnqueueBroadcast(new GameMessageScript(target.Guid, spell.TargetEffect, spell.Formula.Scale));
                enchantmentStatus = CreatureMagic(target, spell);
                if (enchantmentStatus.Message != null)
                {
                    Session.Network.EnqueueSend(enchantmentStatus.Message);
                }

                if (spell.IsHarmful)
                {
                    if (targetCreature != null)
                    {
                        Proficiency.OnSuccessUse(this, GetCreatureSkill(Skill.CreatureEnchantment), targetCreature.GetCreatureSkill(Skill.MagicDefense).Current);
                    }

                    // handle target procs
                    if (targetCreature != null && targetCreature != this)
                    {
                        TryProcEquippedItems(targetCreature, false);
                    }

                    if (targetPlayer != null)
                    {
                        UpdatePKTimers(this, targetPlayer);
                    }
                }
                else
                {
                    Proficiency.OnSuccessUse(this, GetCreatureSkill(Skill.CreatureEnchantment), spell.PowerMod);
                }

                break;

            case MagicSchool.LifeMagic:

                if (targetPlayer == null)
                {
                    OnAttackMonster(targetCreature);
                }

                if (spell.MetaSpellType != SpellType.LifeProjectile)
                {
                    if (spell.IsHarmful)
                    {
                        var resisted = ResistSpell(target, spell);
                        if (resisted == true)
                        {
                            break;
                        }
                        if (resisted == null)
                        {
                            log.Error("Something went wrong with the Magic resistance check");
                            break;
                        }
                    }

                    if (targetCreature != null && targetCreature.NonProjectileMagicImmune)
                    {
                        Session.Network.EnqueueSend(new GameMessageSystemChat($"You fail to affect {targetCreature.Name} with {spell.Name}", ChatMessageType.Magic));
                        break;
                    }
                }

                if (target != null)
                {
                    EnqueueBroadcast(new GameMessageScript(target.Guid, spell.TargetEffect, spell.Formula.Scale));
                }

                targetDeath = LifeMagic(spell, out uint damage, out bool critical, out enchantmentStatus, target);

                if (spell.MetaSpellType != SpellType.LifeProjectile)
                {
                    if (spell.IsHarmful)
                    {
                        if (targetCreature != null)
                        {
                            Proficiency.OnSuccessUse(this, GetCreatureSkill(Skill.LifeMagic), targetCreature.GetCreatureSkill(Skill.MagicDefense).Current);
                        }

                        // handle target procs
                        if (targetCreature != null && targetCreature != this)
                        {
                            TryProcEquippedItems(targetCreature, false);
                        }

                        if (targetPlayer != null)
                        {
                            UpdatePKTimers(this, targetPlayer);
                        }
                    }
                    else
                    {
                        Proficiency.OnSuccessUse(this, GetCreatureSkill(Skill.LifeMagic), spell.PowerMod);
                    }
                }

                if (targetDeath == true)
                {
                    targetCreature.OnDeath(new DamageHistoryInfo(this), DamageType.Health, false);
                    targetCreature.Die();
                }
                else
                {
                    if (enchantmentStatus.Message != null)
                    {
                        Session.Network.EnqueueSend(enchantmentStatus.Message);
                    }
                }
                break;

            case MagicSchool.ItemEnchantment:

                // if negative item spell, can be resisted by the wielder
                if (spell.IsHarmful)
                {
                    var targetResist = targetCreature;

                    if (targetResist == null && target?.WielderId != null)
                    {
                        targetResist = CurrentLandblock?.GetObject(target.WielderId.Value) as Creature;
                    }

                    if (targetResist != null)
                    {
                        var resisted = ResistSpell(targetResist, spell);
                        if (resisted == true)
                        {
                            break;
                        }
                        if (resisted == null)
                        {
                            log.Error("Something went wrong with the Magic resistance check");
                            break;
                        }
                    }
                }

                if (spell.IsImpenBaneType)
                {
                    // impen / bane / brittlemail / lure

                    // a lot of these will already be filtered out by IsInvalidTarget()
                    if (targetCreature == null)
                    {
                        // targeting an individual item / wo
                        enchantmentStatus = ItemMagic(target, spell);

                        if (target != null)
                        {
                            EnqueueBroadcast(new GameMessageScript(target.Guid, spell.TargetEffect, spell.Formula.Scale));
                        }

                        if (enchantmentStatus.Message != null)
                        {
                            Session.Network.EnqueueSend(enchantmentStatus.Message);
                        }
                    }
                    else
                    {
                        // targeting a creature
                        if (targetPlayer == this)
                        {
                            // targeting self
                            var items = EquippedObjects.Values.Where(i => (i.WeenieType == WeenieType.Clothing || i.IsShield) && i.IsEnchantable);

                            foreach (var item in items)
                            {
                                enchantmentStatus = ItemMagic(item, spell);
                                if (enchantmentStatus.Message != null)
                                {
                                    Session.Network.EnqueueSend(enchantmentStatus.Message);
                                }
                            }
                            if (items.Count() > 0)
                            {
                                EnqueueBroadcast(new GameMessageScript(Guid, spell.TargetEffect, spell.Formula.Scale));
                            }
                        }
                        else
                        {
                            // targeting another player or monster
                            var item = targetCreature.EquippedObjects.Values.FirstOrDefault(i => i.IsShield && i.IsEnchantable);

                            if (item != null)
                            {
                                enchantmentStatus = ItemMagic(item, spell);
                                EnqueueBroadcast(new GameMessageScript(item.Guid, spell.TargetEffect, spell.Formula.Scale));
                                if (enchantmentStatus.Message != null)
                                {
                                    Session.Network.EnqueueSend(enchantmentStatus.Message);
                                }
                            }
                            else
                            {
                                // 'fails to affect'?
                                if (targetCreature != null)
                                {
                                    Session.Network.EnqueueSend(new GameMessageSystemChat($"You fail to affect {targetCreature.Name} with {spell.Name}", ChatMessageType.Magic));
                                }

                                if (targetPlayer != null && !targetPlayer.SquelchManager.Squelches.Contains(this, ChatMessageType.Magic))
                                {
                                    targetPlayer.Session.Network.EnqueueSend(new GameMessageSystemChat($"{Name} fails to affect you with {spell.Name}", ChatMessageType.Magic));
                                }
                            }
                        }
                    }
                }
                else if (spell.IsOtherNegativeRedirectable)
                {
                    // blood loather, spirit loather, lure blade, turn blade, leaden weapon, hermetic void
                    if (targetCreature == null)
                    {
                        // targeting an individual item / wo
                        enchantmentStatus = ItemMagic(target, spell);

                        if (target != null)
                        {
                            EnqueueBroadcast(new GameMessageScript(target.Guid, spell.TargetEffect, spell.Formula.Scale));
                        }

                        if (enchantmentStatus.Message != null)
                        {
                            Session.Network.EnqueueSend(enchantmentStatus.Message);
                        }
                    }
                    else
                    {
                        // targeting a creature, try to redirect to primary weapon
                        var weapon = targetCreature.GetEquippedWeapon() ?? targetCreature.GetEquippedWand();

                        if (weapon != null && weapon.IsEnchantable)
                        {
                            enchantmentStatus = ItemMagic(weapon, spell);

                            EnqueueBroadcast(new GameMessageScript(weapon.Guid, spell.TargetEffect, spell.Formula.Scale));

                            if (enchantmentStatus.Message != null)
                            {
                                Session.Network.EnqueueSend(enchantmentStatus.Message);
                            }
                        }
                        else
                        {
                            // 'fails to affect'?
                            Session.Network.EnqueueSend(new GameMessageSystemChat($"You fail to affect {targetCreature.Name} with {spell.Name}", ChatMessageType.Magic));

                            if (targetPlayer != null && !targetPlayer.SquelchManager.Squelches.Contains(this, ChatMessageType.Magic))
                            {
                                targetPlayer.Session.Network.EnqueueSend(new GameMessageSystemChat($"{Name} fails to affect you with {spell.Name}", ChatMessageType.Magic));
                            }
                        }
                    }
                }
                else
                {
                    // all other item spells, cast directly on target
                    enchantmentStatus = ItemMagic(target, spell);

                    if (target != null)
                    {
                        EnqueueBroadcast(new GameMessageScript(target.Guid, spell.TargetEffect, spell.Formula.Scale));
                    }

                    if (enchantmentStatus.Message != null)
                    {
                        Session.Network.EnqueueSend(enchantmentStatus.Message);
                    }
                }

                // use target resistance?
                Proficiency.OnSuccessUse(this, GetCreatureSkill(Skill.ItemEnchantment), spell.PowerMod);

                if (spell.IsHarmful)
                {
                    var playerRedirect = targetPlayer;
                    if (playerRedirect == null && target?.WielderId != null)
                    {
                        playerRedirect = CurrentLandblock?.GetObject(target.WielderId.Value) as Player;
                    }

                    if (playerRedirect != null)
                    {
                        UpdatePKTimers(this, playerRedirect);
                    }
                }
                break;
            }
        }
예제 #15
0
        /// <summary>
        /// Performs a melee attack for the monster
        /// </summary>
        /// <returns>The length in seconds for the attack animation</returns>
        public float MeleeAttack()
        {
            var target = AttackTarget as Creature;

            if (target == null || !target.IsAlive)
            {
                Sleep();
                return(0.0f);
            }

            // choose a random combat maneuver
            var maneuver = GetCombatManeuver();

            if (maneuver == null)
            {
                Console.WriteLine($"Combat maneuver null! Stance {CurrentMotionState.Stance}, MotionTable {MotionTableId:X8}");
                return(0.0f);
            }

            AttackHeight = maneuver.AttackHeight;

            // select random body part @ current attack height
            var bodyPart = BodyParts.GetBodyPart(AttackHeight.Value);

            DoSwingMotion(AttackTarget, maneuver, out float animLength);
            PhysicsObj.stick_to_object(AttackTarget.PhysicsObj.ID);

            var actionChain = new ActionChain();

            actionChain.AddDelaySeconds(animLength / 3.0f);     // TODO: get attack frame?
            actionChain.AddAction(this, () =>
            {
                if (AttackTarget == null)
                {
                    return;
                }

                var critical   = false;
                var damageType = DamageType.Undef;
                var shieldMod  = 1.0f;
                var damage     = CalculateDamage(ref damageType, maneuver, bodyPart, ref critical, ref shieldMod);

                var player = AttackTarget as Player;

                if (damage > 0.0f)
                {
                    player.TakeDamage(this, damageType, damage, bodyPart, critical);

                    if (shieldMod != 1.0f)
                    {
                        var shieldSkill = player.GetCreatureSkill(Skill.Shield);
                        Proficiency.OnSuccessUse(player, shieldSkill, shieldSkill.Current); // ?
                    }
                }
                else
                {
                    player.OnEvade(this, AttackType.Melee);
                }
            });
            actionChain.EnqueueChain();

            // TODO: figure out exact speed / delay formula
            var meleeDelay = Physics.Common.Random.RollDice(MeleeDelayMin, MeleeDelayMax);

            NextAttackTime = Timers.RunningTime + animLength + meleeDelay;;
            return(animLength);
        }
예제 #16
0
파일: Lock.cs 프로젝트: jacobtipp/trACE
        public static void UseUnlocker(Player player, WorldObject unlocker, WorldObject target)
        {
            ActionChain chain = new ActionChain();

            chain.AddAction(player, () =>
            {
                if (unlocker.WeenieType == WeenieType.Lockpick &&
                    player.Skills[Skill.Lockpick].AdvancementClass != SkillAdvancementClass.Trained &&
                    player.Skills[Skill.Lockpick].AdvancementClass != SkillAdvancementClass.Specialized)
                {
                    player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouArentTrainedInLockpicking));
                    return;
                }
                if (target is Lock @lock)
                {
                    UnlockResults result = UnlockResults.IncorrectKey;
                    var difficulty       = 0;
                    if (unlocker.WeenieType == WeenieType.Lockpick)
                    {
                        var effectiveLockpickSkill = GetEffectiveLockpickSkill(player, unlocker);
                        result = @lock.Unlock(player.Guid.Full, effectiveLockpickSkill, ref difficulty);
                    }
                    else if (unlocker is Key woKey)
                    {
                        if (target is Door woDoor)
                        {
                            if (woDoor.LockCode == "") // the door isn't to be opened with keys
                            {
                                player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat));
                                return;
                            }
                        }
                        result = @lock.Unlock(player.Guid.Full, woKey);
                    }

                    switch (result)
                    {
                    case UnlockResults.UnlockSuccess:

                        if (unlocker.WeenieType == WeenieType.Lockpick)
                        {
                            // the source guid for this sound must be the player, else the sound will not play
                            // which differs from PicklockFail and LockSuccess being in the target sound table
                            player.EnqueueBroadcast(new GameMessageSound(player.Guid, Sound.Lockpicking, 1.0f));

                            var lockpickSkill = player.GetCreatureSkill(Skill.Lockpick);
                            Proficiency.OnSuccessUse(player, lockpickSkill, difficulty);
                        }

                        ConsumeUnlocker(player, unlocker, target, true);
                        break;

                    case UnlockResults.Open:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockWhatIsOpen));
                        break;

                    case UnlockResults.AlreadyUnlocked:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.LockAlreadyUnlocked));
                        break;

                    case UnlockResults.PickLockFailed:
                        target.EnqueueBroadcast(new GameMessageSound(target.Guid, Sound.PicklockFail, 1.0f));
                        ConsumeUnlocker(player, unlocker, target, false);
                        break;

                    case UnlockResults.CannotBePicked:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat));
                        break;

                    case UnlockResults.IncorrectKey:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.KeyDoesntFitThisLock));
                        break;
                    }
                }
                else
                {
                    player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat));
                }
            });

            chain.EnqueueChain();
        }
예제 #17
0
        public override void OnCollideObject(WorldObject target)
        {
            //Console.WriteLine($"{Name}.OnCollideObject({target.Name})");

            var player = ProjectileSource as Player;

            if (Info != null && player != null && player.DebugSpell)
            {
                player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{Name}.OnCollideObject({target?.Name} ({target?.Guid}))", ChatMessageType.Broadcast));
                player.Session.Network.EnqueueSend(new GameMessageSystemChat(Info.ToString(), ChatMessageType.Broadcast));
            }

            ProjectileImpact();

            // ensure valid creature target
            var creatureTarget = target as Creature;

            if (creatureTarget == null || target == ProjectileSource)
            {
                return;
            }

            if (player != null)
            {
                player.LastHitSpellProjectile = Spell;
            }

            // ensure caster can damage target
            var sourceCreature = ProjectileSource as Creature;

            if (sourceCreature != null && !sourceCreature.CanDamage(creatureTarget))
            {
                return;
            }

            // if player target, ensure matching PK status
            var targetPlayer = creatureTarget as Player;

            var pkError = CheckPKStatusVsTarget(player, targetPlayer, Spell);

            if (pkError != null)
            {
                if (player != null)
                {
                    player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, pkError[0], creatureTarget.Name));
                }

                if (targetPlayer != null)
                {
                    targetPlayer.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(targetPlayer.Session, pkError[1], ProjectileSource.Name));
                }

                return;
            }

            var critical     = false;
            var critDefended = false;
            var overpower    = false;

            var damage = CalculateDamage(ProjectileSource, Caster, creatureTarget, ref critical, ref critDefended, ref overpower);

            if (damage != null)
            {
                // handle void magic DoTs:
                // instead of instant damage, add DoT to target's enchantment registry
                if (Spell.School == MagicSchool.VoidMagic && Spell.Duration > 0)
                {
                    var dot = ProjectileSource.CreateEnchantment(creatureTarget, ProjectileSource, Spell);
                    if (dot.Message != null && player != null)
                    {
                        player.Session.Network.EnqueueSend(dot.Message);
                    }

                    // corruption / corrosion playscript?
                    //target.EnqueueBroadcast(new GameMessageScript(target.Guid, PlayScript.HealthDownVoid));
                    //target.EnqueueBroadcast(new GameMessageScript(target.Guid, PlayScript.DirtyFightingDefenseDebuff));
                }
                else
                {
                    DamageTarget(creatureTarget, damage.Value, critical, critDefended, overpower);
                }

                if (player != null)
                {
                    Proficiency.OnSuccessUse(player, player.GetCreatureSkill(Spell.School), Spell.PowerMod);
                }

                // handle target procs
                // note that for untargeted multi-projectile spells,
                // ProjectileTarget will be null here, so procs will not apply
                if (sourceCreature != null && ProjectileTarget != null)
                {
                    // Ok... if we got here, we're likely in the parallel landblock physics processing.
                    // We're currently on the thread for this, but we're wanting to perform some work on sourceCreature which can result in a new spell being created
                    // and added to the sourceCreature's current landblock, which, could be on a separate thread.
                    // Any chance of a cross landblock group work (and thus cross thread), should be enqueued onto the target object to maintain thread safety.
                    if (sourceCreature.CurrentLandblock == null || sourceCreature.CurrentLandblock == CurrentLandblock)
                    {
                        sourceCreature.TryProcEquippedItems(creatureTarget, false);
                    }
                    else
                    {
                        sourceCreature.EnqueueAction(new ActionEventDelegate(() => sourceCreature.TryProcEquippedItems(creatureTarget, false)));
                    }
                }
            }

            // also called on resist
            if (player != null && targetPlayer == null)
            {
                player.OnAttackMonster(creatureTarget);
            }
        }
예제 #18
0
        public void OnCollideObject(WorldObject target)
        {
            if (!PhysicsObj.is_active())
            {
                return;
            }

            //Console.WriteLine($"Projectile.OnCollideObject - {WorldObject.Name} ({WorldObject.Guid}) -> {target.Name} ({target.Guid})");

            if (ProjectileTarget == null || ProjectileTarget != target)
            {
                //Console.WriteLine("Unintended projectile target! (should be " + ProjectileTarget.Guid.Full.ToString("X8") + " - " + ProjectileTarget.Name + ")");
                OnCollideEnvironment();
                return;
            }

            // take damage
            var sourceCreature = ProjectileSource as Creature;
            var sourcePlayer   = ProjectileSource as Player;
            var targetCreature = target as Creature;

            DamageEvent damageEvent = null;

            if (targetCreature != null)
            {
                if (sourcePlayer != null)
                {
                    // player damage monster or player
                    damageEvent = sourcePlayer.DamageTarget(targetCreature, WorldObject);

                    if (damageEvent != null && damageEvent.HasDamage)
                    {
                        WorldObject.EnqueueBroadcast(new GameMessageSound(WorldObject.Guid, Sound.Collision, 1.0f));
                    }
                }
                else if (sourceCreature != null && sourceCreature.AttackTarget != null)
                {
                    var targetPlayer = sourceCreature.AttackTarget as Player;

                    damageEvent = DamageEvent.CalculateDamage(sourceCreature, targetCreature, WorldObject);

                    if (targetPlayer != null)
                    {
                        // monster damage player
                        if (damageEvent.HasDamage)
                        {
                            targetPlayer.TakeDamage(sourceCreature, damageEvent);

                            // blood splatter?

                            if (damageEvent.ShieldMod != 1.0f)
                            {
                                var shieldSkill = targetPlayer.GetCreatureSkill(Skill.Shield);
                                Proficiency.OnSuccessUse(targetPlayer, shieldSkill, shieldSkill.Current);   // ??
                            }
                        }
                        else
                        {
                            targetPlayer.OnEvade(sourceCreature, CombatType.Missile);
                        }
                    }
                    else
                    {
                        // monster damage pet
                        if (damageEvent.HasDamage)
                        {
                            targetCreature.TakeDamage(sourceCreature, damageEvent.DamageType, damageEvent.Damage);

                            // blood splatter?
                        }
                    }
                }

                // handle target procs
                if (damageEvent != null && damageEvent.HasDamage)
                {
                    sourceCreature?.TryProcEquippedItems(targetCreature, false);
                }
            }

            WorldObject.CurrentLandblock?.RemoveWorldObject(WorldObject.Guid, showError: !PhysicsObj.entering_world);
            PhysicsObj.set_active(false);

            WorldObject.HitMsg = true;
        }
예제 #19
0
        public override void OnCollideObject(WorldObject target)
        {
            //Console.WriteLine($"{Name}.OnCollideObject({target.Name})");

            var player = ProjectileSource as Player;

            if (Info != null && player != null && player.DebugSpell)
            {
                player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{Name}.OnCollideObject({target?.Name} ({target?.Guid}))", ChatMessageType.Broadcast));
                player.Session.Network.EnqueueSend(new GameMessageSystemChat(Info.ToString(), ChatMessageType.Broadcast));
            }

            ProjectileImpact();

            // ensure valid creature target
            var creatureTarget = target as Creature;

            if (creatureTarget == null || target == ProjectileSource)
            {
                return;
            }

            if (player != null)
            {
                player.LastHitSpellProjectile = Spell;
            }

            // ensure caster can damage target
            var sourceCreature = ProjectileSource as Creature;

            if (sourceCreature != null && !sourceCreature.CanDamage(creatureTarget))
            {
                return;
            }

            // if player target, ensure matching PK status
            var targetPlayer = creatureTarget as Player;

            var pkError = CheckPKStatusVsTarget(player, targetPlayer, Spell);

            if (pkError != null)
            {
                if (player != null)
                {
                    player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, pkError[0], creatureTarget.Name));
                }

                if (targetPlayer != null)
                {
                    targetPlayer.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(targetPlayer.Session, pkError[1], ProjectileSource.Name));
                }

                return;
            }

            var critical     = false;
            var critDefended = false;
            var overpower    = false;

            var damage = CalculateDamage(ProjectileSource, Caster, creatureTarget, ref critical, ref critDefended, ref overpower);

            if (damage != null)
            {
                // handle void magic DoTs:
                // instead of instant damage, add DoT to target's enchantment registry
                if (Spell.School == MagicSchool.VoidMagic && Spell.Duration > 0)
                {
                    var dot = ProjectileSource.CreateEnchantment(creatureTarget, ProjectileSource, Spell);
                    if (dot.Message != null && player != null)
                    {
                        player.Session.Network.EnqueueSend(dot.Message);
                    }

                    // corruption / corrosion playscript?
                    //target.EnqueueBroadcast(new GameMessageScript(target.Guid, PlayScript.HealthDownVoid));
                    //target.EnqueueBroadcast(new GameMessageScript(target.Guid, PlayScript.DirtyFightingDefenseDebuff));
                }
                else
                {
                    DamageTarget(creatureTarget, damage.Value, critical, critDefended, overpower);
                }

                if (player != null)
                {
                    Proficiency.OnSuccessUse(player, player.GetCreatureSkill(Spell.School), Spell.PowerMod);
                }

                // handle target procs
                // note that for untargeted multi-projectile spells,
                // ProjectileTarget will be null here, so procs will not apply
                if (sourceCreature != null && ProjectileTarget != null)
                {
                    sourceCreature.TryProcEquippedItems(creatureTarget, false);
                }
            }

            // also called on resist
            if (player != null && targetPlayer == null)
            {
                player.OnAttackMonster(creatureTarget);
            }
        }
예제 #20
0
파일: Monster_Melee.cs 프로젝트: klp2/ACE
        /// <summary>
        /// Performs a melee attack for the monster
        /// </summary>
        /// <returns>The length in seconds for the attack animation</returns>
        public float MeleeAttack()
        {
            var target       = AttackTarget as Creature;
            var targetPlayer = AttackTarget as Player;
            var targetPet    = AttackTarget as CombatPet;
            var combatPet    = this as CombatPet;

            if (target == null || !target.IsAlive)
            {
                Sleep();
                return(0.0f);
            }

            // choose a random combat maneuver
            var maneuver = GetCombatManeuver();

            if (maneuver == null)
            {
                Console.WriteLine($"Combat maneuver null! Stance {CurrentMotionState.Stance}, MotionTable {MotionTableId:X8}");
                return(0.0f);
            }

            AttackHeight = maneuver.AttackHeight;

            // select random body part @ current attack height
            var bodyPart = BodyParts.GetBodyPart(AttackHeight.Value);

            DoSwingMotion(AttackTarget, maneuver, out float animLength, out var attackFrames);
            PhysicsObj.stick_to_object(AttackTarget.PhysicsObj.ID);

            var numStrikes = attackFrames.Count;

            var actionChain = new ActionChain();

            var prevTime = 0.0f;

            for (var i = 0; i < numStrikes; i++)
            {
                actionChain.AddDelaySeconds(attackFrames[i] * animLength - prevTime);
                prevTime = attackFrames[i] * animLength;

                actionChain.AddAction(this, () =>
                {
                    if (AttackTarget == null)
                    {
                        return;
                    }

                    var critical   = false;
                    var damageType = DamageType.Undef;
                    var shieldMod  = 1.0f;
                    var damage     = CalculateDamage(ref damageType, maneuver, bodyPart, ref critical, ref shieldMod);

                    if (damage != null)
                    {
                        if (combatPet != null || targetPet != null)
                        {
                            // combat pet inflicting or receiving damage
                            //Console.WriteLine($"{target.Name} taking {Math.Round(damage)} {damageType} damage from {Name}");
                            target.TakeDamage(this, damageType, damage.Value);
                            EmitSplatter(target, damage.Value);
                        }
                        else
                        {
                            // this is a player taking damage
                            targetPlayer.TakeDamage(this, damageType, damage.Value, bodyPart, critical);

                            if (shieldMod != 1.0f)
                            {
                                var shieldSkill = targetPlayer.GetCreatureSkill(Skill.Shield);
                                Proficiency.OnSuccessUse(targetPlayer, shieldSkill, shieldSkill.Current); // ?
                            }
                        }
                    }
                    else
                    {
                        target.OnEvade(this, CombatType.Melee);
                    }
                });
            }
            actionChain.EnqueueChain();

            // TODO: figure out exact speed / delay formula
            var meleeDelay = ThreadSafeRandom.Next(MeleeDelayMin, MeleeDelayMax);

            NextAttackTime = Timers.RunningTime + animLength + meleeDelay;
            return(animLength);
        }
예제 #21
0
        /// <summary>
        /// Performs a melee attack for the monster
        /// </summary>
        /// <returns>The length in seconds for the attack animation</returns>
        public float MeleeAttack()
        {
            var player = AttackTarget as Player;

            if (player.Health.Current <= 0)
            {
                return(0.0f);
            }

            // choose a random combat maneuver
            var maneuver = GetCombatManeuver();

            if (maneuver == null)
            {
                return(0.0f);
            }

            AttackHeight = maneuver.AttackHeight;

            // select random body part @ current attack height
            var bodyPart = BodyParts.GetBodyPart(AttackHeight.Value);

            DoSwingMotion(AttackTarget, maneuver, out float animLength);
            PhysicsObj.stick_to_object(AttackTarget.PhysicsObj.ID);

            var actionChain = new ActionChain();

            actionChain.AddDelaySeconds(animLength / 2.0f);
            actionChain.AddAction(this, () =>
            {
                if (AttackTarget == null)
                {
                    return;
                }

                var critical   = false;
                var damageType = DamageType.Undef;
                var shieldMod  = 1.0f;
                var damage     = CalculateDamage(ref damageType, maneuver, bodyPart, ref critical, ref shieldMod);

                if (damage > 0.0f)
                {
                    player.TakeDamage(this, damageType, damage, bodyPart, critical);

                    if (shieldMod != 1.0f)
                    {
                        var shieldSkill = player.GetCreatureSkill(Skill.Shield);
                        Proficiency.OnSuccessUse(player, shieldSkill, shieldSkill.Current); // ?
                    }
                }
                else
                {
                    player.OnEvade(this, AttackType.Melee);
                }
            });
            actionChain.EnqueueChain();

            // TODO: figure out exact speed / delay formula
            NextAttackTime = DateTime.UtcNow.AddSeconds(animLength + MeleeDelay);
            return(animLength);
        }