예제 #1
0
파일: Player.cs 프로젝트: drissical/ACE
        public void SendMessage(string msg, ChatMessageType type = ChatMessageType.Broadcast, WorldObject source = null)
        {
            if (SquelchManager.IsLegalChannel(type) && SquelchManager.Squelches.Contains(source, type))
            {
                return;
            }

            Session.Network.EnqueueSend(new GameMessageSystemChat(msg, type));
        }
예제 #2
0
 /// <summary>
 /// Called when a player runs over the pressure plate
 /// </summary>
 public override void OnCollideObject(WorldObject wo)
 {
     OnActivate(wo);
 }
예제 #3
0
 public override void ActOnUse(WorldObject activator)
 {
     // handled in base.OnActivate -> EmoteManager.OnUse()
 }
예제 #4
0
        /// <summary>
        /// Return the scalar damage absorbed by a shield
        /// </summary>
        public float GetShieldMod(WorldObject attacker, DamageType damageType, WorldObject weapon)
        {
            // ensure combat stance
            if (CombatMode == CombatMode.NonCombat)
            {
                return(1.0f);
            }

            // does the player have a shield equipped?
            var shield = GetEquippedShield();

            if (shield == null)
            {
                return(1.0f);
            }

            // phantom weapons ignore all armor and shields
            if (weapon != null && weapon.HasImbuedEffect(ImbuedEffectType.IgnoreAllArmor))
            {
                return(1.0f);
            }

            // is monster in front of player,
            // within shield effectiveness area?
            var effectiveAngle = 180.0f;
            var angle          = GetAngle(attacker);

            if (Math.Abs(angle) > effectiveAngle / 2.0f)
            {
                return(1.0f);
            }

            // get base shield AL
            var baseSL = shield.GetProperty(PropertyInt.ArmorLevel) ?? 0.0f;

            // shield AL item enchantment additives:
            // impenetrability, brittlemail
            var ignoreMagicArmor = (weapon?.IgnoreMagicArmor ?? false) || (attacker?.IgnoreMagicArmor ?? false);

            var modSL = shield.EnchantmentManager.GetArmorMod();

            if (ignoreMagicArmor)
            {
                modSL = attacker is Player ? (int)Math.Round(IgnoreMagicArmorScaled(modSL)) : 0;
            }

            var effectiveSL = baseSL + modSL;

            // get shield RL against damage type
            var baseRL = GetResistance(shield, damageType);

            // shield RL item enchantment additives:
            // banes, lures
            var modRL = shield.EnchantmentManager.GetArmorModVsType(damageType);

            if (ignoreMagicArmor)
            {
                modRL = attacker is Player?IgnoreMagicArmorScaled(modRL) : 0.0f;
            }

            var effectiveRL = (float)(baseRL + modRL);

            // resistance clamp
            effectiveRL = Math.Clamp(effectiveRL, -2.0f, 2.0f);

            // handle negative SL
            if (effectiveSL < 0)
            {
                effectiveRL = 1.0f / effectiveRL;
            }

            var effectiveLevel = effectiveSL * effectiveRL;

            // SL cap:
            // Trained / untrained: 1/2 shield skill
            // Spec: shield skill
            // SL cap is applied *after* item enchantments
            var shieldSkill = GetCreatureSkill(Skill.Shield);
            var shieldCap   = shieldSkill.Current;

            if (shieldSkill.AdvancementClass != SkillAdvancementClass.Specialized)
            {
                shieldCap = (uint)Math.Round(shieldCap / 2.0f);
            }

            effectiveLevel = Math.Min(effectiveLevel, shieldCap);

            var ignoreShieldMod = attacker.GetIgnoreShieldMod(weapon);

            //Console.WriteLine($"IgnoreShieldMod: {ignoreShieldMod}");

            effectiveLevel *= ignoreShieldMod;

            // SL is multiplied by existing AL
            var shieldMod = SkillFormula.CalcArmorMod(effectiveLevel);

            //Console.WriteLine("ShieldMod: " + shieldMod);
            return(shieldMod);
        }
예제 #5
0
        public void FightDirty(WorldObject target)
        {
            // Skill description:
            // Your melee and missile attacks have a chance to weaken your opponent.
            // - Low attacks can reduce the defense skills of the opponent.
            // - Medium attacks can cause small amounts of bleeding damage.
            // - High attacks can reduce opponents' attack and healing skills

            // Effects:
            // Low: reduces the defense skills of the opponent by -10
            // Medium: bleed ticks for 60 damage per 20 seconds
            // High: reduces the attack skills of the opponent by -10, and
            //       the healing effects of the opponent by -15 rating
            //
            // these damage #s are doubled for dirty fighting specialized.

            // Notes:
            // - Dirty fighting works for melee and missile attacks.
            // - Has a 25% chance to activate on any melee of missile attack.
            //   - This activation is reduced proportionally if Dirty Fighting is lower
            //     than your active weapon skill as determined by your equipped weapon.
            // - All activate effects last 20 seconds.
            // - Although a specific effect won't stack with itself,
            //   you can stack all 3 effects on the opponent at the same time. This means
            //   when a skill activates at one attack height, you can move to another attack height
            //   to try to land an additional effect.
            // - Successfully landing a Dirty Fighting effect is mentioned in chat. Additionally,
            //   the medium height effect results in 'floating glyphs' around the target:

            //   "Dirty Fighting! <Player> delivers a Bleeding Assault to <target>!"
            //   "Dirty Fighting! <Player> delivers a Traumatic Assault to <target>!"

            // dirty fighting skill must be at least trained
            var dirtySkill = GetCreatureSkill(Skill.DirtyFighting);

            if (dirtySkill.AdvancementClass < SkillAdvancementClass.Trained)
            {
                return;
            }

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

            if (creatureTarget == null)
            {
                return;
            }

            var chance = 0.25f;

            var attackSkill = GetCreatureSkill(GetCurrentWeaponSkill());

            if (dirtySkill.Current < attackSkill.Current)
            {
                chance *= (float)dirtySkill.Current / attackSkill.Current;
            }

            var rng = ThreadSafeRandom.Next(0.0f, 1.0f);

            if (rng > chance)
            {
                return;
            }

            switch (AttackHeight)
            {
            case ACE.Entity.Enum.AttackHeight.Low:
                FightDirty_ApplyLowAttack(creatureTarget);
                break;

            case ACE.Entity.Enum.AttackHeight.Medium:
                FightDirty_ApplyMediumAttack(creatureTarget);
                break;

            case ACE.Entity.Enum.AttackHeight.High:
                FightDirty_ApplyHighAttack(creatureTarget);
                break;
            }
        }
예제 #6
0
 /// <summary>
 /// Returns a value between 0.6-1.6 for bow attacks,
 /// depending on the accuracy meter
 /// </summary>
 public virtual float GetAccuracyMod(WorldObject weapon)
 {
     // doesn't apply for non-player creatures?
     return(1.0f);
 }
예제 #7
0
 /// <summary>
 /// Called when a creature hits a target
 /// </summary>
 public virtual void OnDamageTarget(WorldObject target, CombatType attackType, bool critical)
 {
     // empty base for non-player creatures?
 }
예제 #8
0
        /// <summary>
        /// Return the scalar damage absorbed by a shield
        /// </summary>
        public float GetShieldMod(WorldObject attacker, DamageType damageType)
        {
            // does the player have a shield equipped?
            var shield = GetEquippedShield();

            if (shield == null)
            {
                return(1.0f);
            }

            // is monster in front of player,
            // within shield effectiveness area?
            var effectiveAngle = 180.0f;
            var angle          = GetAngle(attacker);

            if (Math.Abs(angle) > effectiveAngle / 2.0f)
            {
                return(1.0f);
            }

            // get base shield AL
            var baseSL = shield.GetProperty(PropertyInt.ArmorLevel) ?? 0.0f;

            // shield AL item enchantment additives:
            // impenetrability, brittlemail
            var modSL       = shield.EnchantmentManager.GetArmorMod();
            var effectiveSL = baseSL + modSL;

            // get shield RL against damage type
            var baseRL = GetResistance(shield, damageType);

            // shield RL item enchantment additives:
            // banes, lures
            var modRL       = shield.EnchantmentManager.GetArmorModVsType(damageType);
            var effectiveRL = (float)(baseRL + modRL);

            // resistance cap
            if (effectiveRL > 2.0f)
            {
                effectiveRL = 2.0f;
            }

            var effectiveLevel = effectiveSL * effectiveRL;

            // SL cap:
            // Trained / untrained: 1/2 shield skill
            // Spec: shield skill
            // SL cap is applied *after* item enchantments
            var shieldSkill = GetCreatureSkill(Skill.Shield);
            var shieldCap   = shieldSkill.Current;

            if (shieldSkill.AdvancementClass != SkillAdvancementClass.Specialized)
            {
                shieldCap = (uint)Math.Round(shieldCap / 2.0f);
            }

            effectiveLevel = Math.Min(effectiveLevel, shieldCap);

            // SL is multiplied by existing AL
            var shieldMod = SkillFormula.CalcArmorMod(effectiveLevel);

            //Console.WriteLine("ShieldMod: " + shieldMod);
            return(shieldMod);
        }
예제 #9
0
        /// <summary>
        /// This method sets properties needed for items that will be child items.
        /// Items here are only items equipped in the hands.
        /// This deals with the orientation and positioning for visual appearance of the child items held by the parent. Og II
        /// </summary>
        /// <param name="item">The child item - we link them together</param>
        /// <param name="placementPosition">Where is this on the parent - where is it equipped</param>
        /// <param name="placementId">out parameter - this deals with the orientation of the child item as it relates to parent model</param>
        /// <param name="parentLocation">out parameter - this is another part of the orientation data for correct visual display</param>
        protected void SetChild(WorldObject item, int placementPosition, out int placementId, out int parentLocation)
        {
            placementId    = 0;
            parentLocation = 0;

            // TODO: I think there is a state missing - it is one of the edge cases. I need to revist this.   Og II
            switch ((EquipMask)placementPosition)
            {
            case EquipMask.MeleeWeapon:
                placementId    = (int)ACE.Entity.Enum.Placement.RightHandCombat;
                parentLocation = (int)ACE.Entity.Enum.ParentLocation.RightHand;
                break;

            case EquipMask.Shield:
                if (item.ItemType == ItemType.Armor)
                {
                    placementId    = (int)ACE.Entity.Enum.Placement.Shield;
                    parentLocation = (int)ACE.Entity.Enum.ParentLocation.Shield;
                }
                else
                {
                    placementId    = (int)ACE.Entity.Enum.Placement.RightHandCombat;
                    parentLocation = (int)ACE.Entity.Enum.ParentLocation.LeftWeapon;
                }
                break;

            case EquipMask.MissileWeapon:
                if (item.DefaultCombatStyle == CombatStyle.Bow ||
                    item.DefaultCombatStyle == CombatStyle.Crossbow)
                {
                    placementId    = (int)ACE.Entity.Enum.Placement.LeftHand;
                    parentLocation = (int)ACE.Entity.Enum.ParentLocation.LeftHand;
                }
                else
                {
                    placementId    = (int)ACE.Entity.Enum.Placement.RightHandCombat;
                    parentLocation = (int)ACE.Entity.Enum.ParentLocation.RightHand;
                }
                break;

            case EquipMask.MissileAmmo:
                throw new NotImplementedException();
                break;

            case EquipMask.Held:
                placementId    = (int)ACE.Entity.Enum.Placement.RightHandCombat;
                parentLocation = (int)ACE.Entity.Enum.ParentLocation.RightHand;
                break;

            default:
                placementId    = (int)ACE.Entity.Enum.Placement.Default;
                parentLocation = (int)ACE.Entity.Enum.ParentLocation.None;
                break;
            }

            if (item.CurrentWieldedLocation != null)
            {
                Children.Add(new HeldItem(item.Guid.Full, parentLocation, (EquipMask)item.CurrentWieldedLocation));
            }

            item.Placement      = (Placement)placementId;
            item.ParentLocation = (ParentLocation)parentLocation;
            item.Location       = Location;
        }
예제 #10
0
        /// <summary>
        /// Creates an enchantment and interacts with the Enchantment registry.
        /// Used by Life, Creature, Item, and Void magic
        /// </summary>
        /// <param name="target"></param>
        /// <param name="spell"></param>
        /// <param name="spellStatMod"></param>
        /// <param name="castByItem"></param>
        /// <returns></returns>
        private string CreateEnchantment(WorldObject target, SpellBase spell, Database.Models.World.Spell spellStatMod, string castByItem = null)
        {
            double duration;

            if (castByItem == null)
            {
                duration = -1;
            }
            else
            {
                duration = spell.Duration;
            }
            // create enchantment
            var enchantment = new Enchantment(target, spellStatMod.SpellId, duration, 1, (uint)EnchantmentMask.CreatureSpells);
            var stackType   = target.EnchantmentManager.Add(enchantment, castByItem);

            var player         = this as Player;
            var playerTarget   = target as Player;
            var creatureTarget = target as Creature;

            // build message
            var suffix = "";

            switch (stackType)
            {
            case StackType.Refresh:
                suffix = $", refreshing {spell.Name}";
                break;

            case StackType.Surpass:
                suffix = $", surpassing {target.EnchantmentManager.Surpass.Name}";
                break;

            case StackType.Surpassed:
                suffix = $", but it is surpassed by {target.EnchantmentManager.Surpass.Name}";
                break;
            }

            var targetName = this == target ? "yourself" : target.Name;

            string message;

            if (castByItem != null)
            {
                message = $"{castByItem} casts {spell.Name} on you";
            }
            else
            {
                message = $"You cast {spell.Name} on {targetName}{suffix}";
            }


            if (target is Player)
            {
                if (stackType != StackType.Surpassed)
                {
                    playerTarget.Session.Network.EnqueueSend(new GameEventMagicUpdateEnchantment(playerTarget.Session, enchantment));
                }

                if (playerTarget != this)
                {
                    playerTarget.Session.Network.EnqueueSend(new GameMessageSystemChat($"{Name} cast {spell.Name} on you{suffix}", ChatMessageType.Magic));
                }
            }

            return(message);
        }
예제 #11
0
        /// <summary>
        /// Creates the Magic projectile spells for Life, War, and Void Magic
        /// </summary>
        /// <param name="caster"></param>
        /// <param name="target"></param>
        /// <param name="spellId"></param>
        /// <param name="projectileWcid"></param>
        /// <param name="lifeProjectileDamage"></param>
        private void CreateSpellProjectile(WorldObject caster, WorldObject target, uint spellId, uint projectileWcid, uint lifeProjectileDamage = 0)
        {
            SpellProjectile spellProjectile = WorldObjectFactory.CreateNewWorldObject(projectileWcid) as SpellProjectile;

            spellProjectile.Setup(spellId);

            var origin = caster.Location.ToGlobal();

            if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Arc)
            {
                origin.Z += caster.Height;
            }
            else
            {
                origin.Z += caster.Height * 2.0f / 3.0f;
            }

            var dest = target.Location.ToGlobal();

            dest.Z += target.Height / 2.0f;

            var direction = Vector3.Normalize(dest - origin);

            // This is not perfect but is close to values that retail used. TODO: revisit this later.
            origin += direction * (caster.PhysicsObj.GetRadius() + spellProjectile.PhysicsObj.GetRadius());

            float time;
            var   dist  = (dest - origin).Length();
            float speed = 15f;

            if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Bolt)
            {
                speed = GetStationaryVelocity(15f, dist);
            }
            else if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Streak)
            {
                speed = GetStationaryVelocity(45f, dist);
            }
            else if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Arc)
            {
                speed = GetStationaryVelocity(40f, dist);
            }

            // TODO: Implement target leading for non arc spells
            // Also: velocity seems to increase when target is moving away from the caster and decrease when
            // the target is moving toward the caster. This still needs more research.

            var velocity = direction * speed;

            var useGravity = spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Arc;

            spellProjectile.Velocity   = GetSpellProjectileVelocity(origin, target, dest, speed, useGravity, out time);
            spellProjectile.FlightTime = time;

            var loc = caster.Location;

            origin = loc.Pos;
            if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Arc)
            {
                origin.Z += caster.Height;
            }
            else
            {
                origin.Z += caster.Height * 2.0f / 3.0f;
            }
            origin += direction * (caster.PhysicsObj.GetRadius() + spellProjectile.PhysicsObj.GetRadius());

            spellProjectile.Location             = new ACE.Entity.Position(loc.LandblockId.Raw, origin.X, origin.Y, origin.Z, loc.Rotation.X, loc.Rotation.Y, loc.Rotation.Z, loc.RotationW);
            spellProjectile.ParentWorldObject    = (Creature)this;
            spellProjectile.TargetGuid           = target.Guid;
            spellProjectile.LifeProjectileDamage = lifeProjectileDamage;
            spellProjectile.ProjectileSource     = caster;
            spellProjectile.ProjectileTarget     = target;
            spellProjectile.SetProjectilePhysicsState(target, useGravity);

            LandblockManager.AddObject(spellProjectile);
            CurrentLandblock.EnqueueBroadcast(spellProjectile.Location, new GameMessageScript(spellProjectile.Guid, ACE.Entity.Enum.PlayScript.Launch, spellProjectile.PlayscriptIntensity));

            // detonate point-blank projectiles immediately
            var radsum = target.PhysicsObj.GetRadius() + spellProjectile.PhysicsObj.GetRadius();

            if (dist < radsum)
            {
                spellProjectile.OnCollideObject(target);
            }

            // TODO : removed when real server projectile tracking and collisions are implemented

            /*var actionChain = new ActionChain();
             * actionChain.AddDelaySeconds(spellProjectile.FlightTime);
             * actionChain.AddAction(spellProjectile, () => spellProjectile.HandleOnCollide(spellProjectile.TargetGuid));
             * actionChain.EnqueueChain();*/
        }
예제 #12
0
 /// <summary>
 /// Wrapper around CreateEnchantment for Creature Magic
 /// </summary>
 /// <param name="target"></param>
 /// <param name="spell"></param>
 /// <param name="spellStatMod"></param>
 /// <param name="castByItem"></param>
 /// <returns></returns>
 protected string CreatureMagic(WorldObject target, SpellBase spell, Database.Models.World.Spell spellStatMod, string castByItem = null)
 {
     return(CreateEnchantment(target, spell, spellStatMod, castByItem));
 }
예제 #13
0
        /// <summary>
        /// Creates the Life Magic spell
        /// </summary>
        /// <param name="target"></param>
        /// <param name="spell"></param>
        /// <param name="spellStatMod"></param>
        /// <param name="message"></param>
        /// <param name="castByItem"></param>
        /// <returns></returns>
        protected bool LifeMagic(WorldObject target, SpellBase spell, Database.Models.World.Spell spellStatMod, out string message, string castByItem = null)
        {
            string srcVital, destVital, action;
            string targetMsg = null;

            Player   player   = null;
            Creature creature = null;

            if (WeenieClassId == 1)
            {
                player = (Player)this;
            }
            else if (WeenieType == WeenieType.Creature)
            {
                creature = (Creature)this;
            }

            Creature spellTarget;

            if (spell.BaseRangeConstant == 0)
            {
                spellTarget = (Creature)this;
            }
            else
            {
                spellTarget = (Creature)target;
            }

            int newSpellTargetVital;

            switch (spell.MetaSpellType)
            {
            case SpellType.Boost:
                int minBoostValue, maxBoostValue;
                if ((spellStatMod.BoostVariance + spellStatMod.Boost) < spellStatMod.Boost)
                {
                    minBoostValue = (int)(spellStatMod.BoostVariance + spellStatMod.Boost);
                    maxBoostValue = (int)spellStatMod.Boost;
                }
                else
                {
                    minBoostValue = (int)spellStatMod.Boost;
                    maxBoostValue = (int)(spellStatMod.BoostVariance + spellStatMod.Boost);
                }
                int boost = Physics.Common.Random.RollDice(minBoostValue, maxBoostValue);
                if (boost < 0)
                {
                    action = "drain";
                }
                else
                {
                    action = "restore";
                }
                switch (spellStatMod.DamageType)
                {
                case 512:           // Mana
                    newSpellTargetVital = (int)(spellTarget.Mana.Current + boost);
                    srcVital            = "mana";
                    if (newSpellTargetVital < spellTarget.Mana.MaxValue)
                    {
                        if (newSpellTargetVital <= 0)
                        {
                            spellTarget.UpdateVital(spellTarget.Mana, 0);
                        }
                        else
                        {
                            spellTarget.UpdateVital(spellTarget.Mana, (uint)newSpellTargetVital);
                        }
                    }
                    else
                    {
                        spellTarget.UpdateVital(spellTarget.Mana, spellTarget.Mana.MaxValue);
                    }
                    break;

                case 256:           // Stamina
                    newSpellTargetVital = (int)(spellTarget.Stamina.Current + boost);
                    srcVital            = "stamina";
                    if (newSpellTargetVital < spellTarget.Stamina.MaxValue)
                    {
                        if (newSpellTargetVital <= 0)
                        {
                            spellTarget.UpdateVital(spellTarget.Stamina, 0);
                        }
                        else
                        {
                            spellTarget.UpdateVital(spellTarget.Stamina, (uint)newSpellTargetVital);
                        }
                    }
                    else
                    {
                        spellTarget.UpdateVital(spellTarget.Stamina, spellTarget.Stamina.MaxValue);
                    }
                    break;

                default:           // Health
                    newSpellTargetVital = (int)(spellTarget.Health.Current + boost);
                    srcVital            = "health";
                    if (newSpellTargetVital < spellTarget.Health.MaxValue)
                    {
                        if (newSpellTargetVital <= 0)
                        {
                            spellTarget.UpdateVital(spellTarget.Health, 0);
                        }
                        else
                        {
                            spellTarget.UpdateVital(spellTarget.Health, (uint)newSpellTargetVital);
                        }
                    }
                    else
                    {
                        spellTarget.UpdateVital(spellTarget.Health, spellTarget.Health.MaxValue);
                    }
                    break;
                }
                if (this is Player)
                {
                    if (spell.BaseRangeConstant == 0)
                    {
                        message = $"You {action} {Math.Abs(boost).ToString()} {srcVital}";
                    }
                    else
                    {
                        message = $"You {action} {Math.Abs(boost).ToString()} points of {srcVital} from {spellTarget.Name}";
                    }
                }
                else
                {
                    message = null;
                }

                if (target is Player && spell.BaseRangeConstant > 0)
                {
                    targetMsg = $"{Name} casts {spell.Name} and {action}s {Math.Abs(boost)} points of your {srcVital}.";
                }

                break;

            case SpellType.Transfer:
                // Calculate the change in vitals of the target
                Creature caster;
                if (spell.BaseRangeConstant == 0 && spell.BaseRangeMod == 1)
                {
                    caster = spellTarget;
                }
                else
                {
                    caster = (Creature)this;
                }
                uint           vitalChange, casterVitalChange;
                ResistanceType resistanceDrain, resistanceBoost;
                if (spellStatMod.Source == (int)PropertyAttribute2nd.Mana)
                {
                    resistanceDrain = ResistanceType.ManaDrain;
                }
                else if (spellStatMod.Source == (int)PropertyAttribute2nd.Stamina)
                {
                    resistanceDrain = ResistanceType.StaminaDrain;
                }
                else
                {
                    resistanceDrain = ResistanceType.HealthDrain;
                }
                vitalChange = (uint)((spellTarget.GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source) * spellStatMod.Proportion) * spellTarget.GetNaturalResistence(resistanceDrain));
                if (spellStatMod.TransferCap != 0)
                {
                    if (vitalChange > spellStatMod.TransferCap)
                    {
                        vitalChange = (uint)spellStatMod.TransferCap;
                    }
                }
                if (spellStatMod.Destination == (int)PropertyAttribute2nd.Mana)
                {
                    resistanceBoost = ResistanceType.ManaDrain;
                }
                else if (spellStatMod.Source == (int)PropertyAttribute2nd.Stamina)
                {
                    resistanceBoost = ResistanceType.StaminaDrain;
                }
                else
                {
                    resistanceBoost = ResistanceType.HealthDrain;
                }
                casterVitalChange   = (uint)((vitalChange * (1.0f - spellStatMod.LossPercent)) * spellTarget.GetNaturalResistence(resistanceBoost));
                vitalChange         = (uint)(casterVitalChange / (1.0f - spellStatMod.LossPercent));
                newSpellTargetVital = (int)(spellTarget.GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source) - vitalChange);

                // Apply the change in vitals to the target
                switch (spellStatMod.Source)
                {
                case (int)PropertyAttribute2nd.Mana:
                    srcVital = "mana";
                    if (newSpellTargetVital <= 0)
                    {
                        spellTarget.UpdateVital(spellTarget.Mana, 0);
                    }
                    else
                    {
                        spellTarget.UpdateVital(spellTarget.Mana, (uint)newSpellTargetVital);
                    }
                    break;

                case (int)PropertyAttribute2nd.Stamina:
                    srcVital = "stamina";
                    if (newSpellTargetVital <= 0)
                    {
                        spellTarget.UpdateVital(spellTarget.Stamina, 0);
                    }
                    else
                    {
                        spellTarget.UpdateVital(spellTarget.Stamina, (uint)newSpellTargetVital);
                    }
                    break;

                default:           // Health
                    srcVital = "health";
                    if (newSpellTargetVital <= 0)
                    {
                        spellTarget.UpdateVital(spellTarget.Health, 0);
                    }
                    else
                    {
                        spellTarget.UpdateVital(spellTarget.Health, (uint)newSpellTargetVital);
                    }
                    break;
                }

                // Apply the scaled change in vitals to the caster
                uint newCasterVital;
                switch (spellStatMod.Destination)
                {
                case (int)PropertyAttribute2nd.Mana:
                    destVital      = "mana";
                    newCasterVital = caster.Mana.Current + casterVitalChange;
                    caster.UpdateVital(caster.Mana, newCasterVital);
                    break;

                case (int)PropertyAttribute2nd.Stamina:
                    destVital      = "stamina";
                    newCasterVital = caster.Stamina.Current + casterVitalChange;
                    caster.UpdateVital(caster.Stamina, newCasterVital);
                    break;

                default:           // Health
                    destVital      = "health";
                    newCasterVital = caster.Mana.Current + casterVitalChange;
                    caster.UpdateVital(caster.Health, newCasterVital);
                    break;
                }

                if (WeenieClassId == 1)
                {
                    if (target.Guid == Guid)
                    {
                        message = $"You drain {vitalChange.ToString()} points of {srcVital} and apply {casterVitalChange.ToString()} points of {destVital} to yourself";
                    }
                    else
                    {
                        message = $"You drain {vitalChange.ToString()} points of {srcVital} from {spellTarget.Name} and apply {casterVitalChange.ToString()} to yourself";
                    }
                }
                else
                {
                    message = null;
                }

                if (target is Player && target != this)
                {
                    targetMsg = $"You lose {vitalChange} points of {srcVital} due to {Name} casting {spell.Name} on you";
                }

                break;

            case SpellType.LifeProjectile:
                caster = (Creature)this;
                uint damage;
                if (spell.Name.Contains("Blight"))
                {
                    damage         = (uint)(caster.GetCurrentCreatureVital(PropertyAttribute2nd.Mana) * caster.GetNaturalResistence(ResistanceType.ManaDrain));
                    newCasterVital = caster.Mana.Current - damage;
                    if (newCasterVital <= 0)
                    {
                        caster.UpdateVital(caster.Mana, 0);
                    }
                    else
                    {
                        caster.UpdateVital(caster.Mana, (uint)newCasterVital);
                    }
                }
                else if (spell.Name.Contains("Tenacity"))
                {
                    damage         = (uint)(spellTarget.GetCurrentCreatureVital(PropertyAttribute2nd.Stamina) * spellTarget.GetNaturalResistence(ResistanceType.StaminaDrain));
                    newCasterVital = caster.Stamina.Current - damage;
                    if (newCasterVital <= 0)
                    {
                        caster.UpdateVital(caster.Stamina, 0);
                    }
                    else
                    {
                        caster.UpdateVital(caster.Stamina, (uint)newCasterVital);
                    }
                }
                else
                {
                    damage         = (uint)(spellTarget.GetCurrentCreatureVital(PropertyAttribute2nd.Stamina) * spellTarget.GetNaturalResistence(ResistanceType.HealthDrain));
                    newCasterVital = caster.Health.Current - damage;
                    if (newCasterVital <= 0)
                    {
                        caster.UpdateVital(caster.Health, 0);
                    }
                    else
                    {
                        caster.UpdateVital(caster.Health, (uint)newCasterVital);
                    }
                }

                CreateSpellProjectile(this, target, spell.MetaSpellId, (uint)spellStatMod.Wcid, damage);

                if (caster.Health.Current <= 0)
                {
                    caster.Die();

                    if (caster.WeenieClassId == 1)
                    {
                        Strings.DeathMessages.TryGetValue(DamageType.Base, out var messages);
                        player.Session.Network.EnqueueSend(new GameMessageSystemChat("You have killed yourself", ChatMessageType.Broadcast));
                    }
                }
                message = null;
                break;

            case SpellType.Dispel:
                message = "Spell not implemented, yet!";
                break;

            case SpellType.Enchantment:
                message = CreateEnchantment(target, spell, spellStatMod, castByItem);
                break;

            default:
                message = "Spell not implemented, yet!";
                break;
            }

            if (targetMsg != null)
            {
                var playerTarget = target as Player;
                playerTarget.Session.Network.EnqueueSend(new GameMessageSystemChat(targetMsg, ChatMessageType.Magic));
            }

            if (spellTarget.Health.Current == 0)
            {
                return(true);
            }
            else
            {
                return(false);
            }
        }
예제 #14
0
        /// <summary>
        /// Determines whether the target for the spell being cast is invalid
        /// </summary>
        /// <param name="spell"></param>
        /// <param name="target"></param>
        /// <returns></returns>
        protected bool IsInvalidTarget(SpellBase spell, WorldObject target)
        {
            // Self targeted spells should have a target of self
            if ((int)Math.Floor(spell.BaseRangeConstant) == 0 && target.WeenieClassId != 1)
            {
                return(true);
            }

            // Invalidate non Item Enchantment spells cast against non Creatures or Players
            if (spell.School != MagicSchool.ItemEnchantment)
            {
                if (target.WeenieType != WeenieType.Creature)
                {
                    if (target.WeenieClassId != 1)
                    {
                        return(true);
                    }
                }
            }

            // Invalidate beneficial spells against Creature/Non-player targets
            if (target.WeenieType == WeenieType.Creature && IsSpellHarmful(spell) == false)
            {
                return(true);
            }

            // Cannot cast Weapon Aura spells on targets that are not players or creatures
            if ((spell.Name.Contains("Aura of")) && (spell.School == MagicSchool.ItemEnchantment))
            {
                if (target.WeenieClassId != 1)
                {
                    if (target.WeenieType != WeenieType.Creature)
                    {
                        return(true);
                    }
                }
            }

            // Cannot cast Weapon Aura spells on targets that are not players or creatures
            if ((spell.MetaSpellType == SpellType.Enchantment) && (spell.School == MagicSchool.ItemEnchantment))
            {
                if ((target.WeenieClassId == 1) ||
                    (target.WeenieType == WeenieType.Creature) ||
                    (target.WeenieType == WeenieType.Clothing) ||
                    (target.WeenieType == WeenieType.Caster) ||
                    (target.WeenieType == WeenieType.MeleeWeapon) ||
                    (target.WeenieType == WeenieType.MissileLauncher) ||
                    (target.WeenieType == WeenieType.Missile) ||
                    (target.WeenieType == WeenieType.Door) ||
                    (target.WeenieType == WeenieType.Chest))
                {
                    return(false);
                }
                else
                {
                    return(true);
                }
            }

            return(false);
        }
예제 #15
0
 /// <summary>
 /// Returns TRUE if the creature receives a +5 DR bonus for this weapon type
 /// </summary>
 public virtual bool GetHeritageBonus(WorldObject weapon)
 {
     // only for players
     return(false);
 }
예제 #16
0
        private List <WorldObject> SpendCurrency(uint amount, WeenieType type)
        {
            if (type == WeenieType.Coin && amount > CoinValue)
            {
                return(null);
            }

            List <WorldObject> currency = new List <WorldObject>();

            currency.AddRange(GetInventoryItemsOfTypeWeenieType(type));
            currency = currency.OrderBy(o => o.Value).ToList();

            List <WorldObject> cost = new List <WorldObject>();
            uint payment            = 0;

            WorldObject changeobj = WorldObjectFactory.CreateNewWorldObject(273);
            uint        change    = 0;

            foreach (WorldObject wo in currency)
            {
                if (payment + wo.StackSize.Value <= amount)
                {
                    // add to payment
                    payment = payment + (uint)wo.StackSize.Value;
                    cost.Add(wo);
                }
                else if (payment + wo.StackSize.Value > amount)
                {
                    // add payment
                    payment = payment + (uint)wo.StackSize.Value;
                    cost.Add(wo);
                    // calculate change
                    if (payment > amount)
                    {
                        change = payment - amount;
                        // add new change object.
                        changeobj.SetStackSize((int)change);
                        wo.SetStackSize(wo.StackSize - (int)change);
                    }
                    break;
                }
                else if (payment == amount)
                {
                    break;
                }
            }

            // destroy all stacks of currency required / sale
            foreach (WorldObject wo in cost)
            {
                TryConsumeFromInventoryWithNetworking(wo);
            }

            // if there is change - readd - do this at the end to try to prevent exploiting
            if (change > 0)
            {
                TryCreateInInventoryWithNetworking(changeobj);
            }

            UpdateCoinValue(false);

            return(cost);
        }
예제 #17
0
        /// <summary>
        /// Translates the default combat style for a weapon
        /// into a combat motion stance
        /// </summary>
        public MotionStance GetWeaponStance(WorldObject weapon)
        {
            var combatStance = MotionStance.HandCombat;

            switch (weapon.DefaultCombatStyle)
            {
            case CombatStyle.Atlatl:
                combatStance = MotionStance.AtlatlCombat;
                break;

            case CombatStyle.Bow:
                combatStance = MotionStance.BowCombat;
                break;

            case CombatStyle.Crossbow:
                combatStance = MotionStance.CrossbowCombat;
                break;

            case CombatStyle.DualWield:
                combatStance = MotionStance.DualWieldCombat;
                break;

            case CombatStyle.Magic:
                combatStance = MotionStance.Magic;
                break;

            case CombatStyle.OneHanded:
                combatStance = MotionStance.SwordCombat;
                break;

            case CombatStyle.OneHandedAndShield:
                combatStance = MotionStance.SwordShieldCombat;
                break;

            case CombatStyle.Sling:
                combatStance = MotionStance.SlingCombat;
                break;

            case CombatStyle.ThrownShield:
                combatStance = MotionStance.ThrownShieldCombat;
                break;

            case CombatStyle.ThrownWeapon:
                combatStance = MotionStance.ThrownWeaponCombat;
                break;

            case CombatStyle.TwoHanded:
                // MotionStance.TwoHandedStaffCombat doesn't appear to do anything
                // Additionally, PropertyInt.WeaponType isn't always included, and the 2handed weapons that do appear to use WeaponType.TwoHanded
                combatStance = MotionStance.TwoHandedSwordCombat;
                break;

            case CombatStyle.Unarmed:
                combatStance = MotionStance.HandCombat;
                break;

            default:
                Console.WriteLine($"{Name}.GetCombatStance() - {weapon.DefaultCombatStyle}");
                break;
            }
            return(combatStance);
        }
예제 #18
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);
                }
            }
        }
예제 #19
0
 /// <summary>
 /// Called when a creature evades an attack
 /// </summary>
 public virtual void OnEvade(WorldObject attacker, CombatType attackType)
 {
     // empty base for non-player creatures?
 }
예제 #20
0
        /// <summary>
        /// Calculates the damage for a spell projectile
        /// Used by war magic, void magic, and life magic projectiles
        /// </summary>
        public float?CalculateDamage(WorldObject source, WorldObject caster, Creature target, ref bool criticalHit, ref bool critDefended, ref bool overpower)
        {
            var sourcePlayer = source as Player;
            var targetPlayer = target as Player;

            if (source == null || !target.IsAlive || targetPlayer != null && targetPlayer.Invincible)
            {
                return(null);
            }

            // check lifestone protection
            if (targetPlayer != null && targetPlayer.UnderLifestoneProtection)
            {
                if (sourcePlayer != null)
                {
                    sourcePlayer.Session.Network.EnqueueSend(new GameMessageSystemChat($"The Lifestone's magic protects {targetPlayer.Name} from the attack!", ChatMessageType.Magic));
                }

                targetPlayer.HandleLifestoneProtection();
                return(null);
            }

            var critDamageBonus     = 0.0f;
            var weaponCritDamageMod = 1.0f;
            var weaponResistanceMod = 1.0f;
            var resistanceMod       = 1.0f;

            // life magic
            var lifeMagicDamage = 0.0f;

            // war/void magic
            var baseDamage  = 0;
            var skillBonus  = 0.0f;
            var finalDamage = 0.0f;

            var resistanceType = Creature.GetResistanceType(Spell.DamageType);

            var sourceCreature = source as Creature;

            if (sourceCreature?.Overpower != null)
            {
                overpower = Creature.GetOverpower(sourceCreature, target);
            }

            var resisted = source.TryResistSpell(target, Spell, caster, true);

            if (resisted && !overpower)
            {
                return(null);
            }

            CreatureSkill attackSkill = null;

            if (sourceCreature != null)
            {
                attackSkill = sourceCreature.GetCreatureSkill(Spell.School);
            }

            // critical hit
            var criticalChance = GetWeaponMagicCritFrequency(sourceCreature, attackSkill, target);

            if (ThreadSafeRandom.Next(0.0f, 1.0f) < criticalChance)
            {
                if (targetPlayer != null && targetPlayer.AugmentationCriticalDefense > 0)
                {
                    var criticalDefenseMod    = sourcePlayer != null ? 0.05f : 0.25f;
                    var criticalDefenseChance = targetPlayer.AugmentationCriticalDefense * criticalDefenseMod;

                    if (criticalDefenseChance > ThreadSafeRandom.Next(0.0f, 1.0f))
                    {
                        critDefended = true;
                    }
                }

                if (!critDefended)
                {
                    criticalHit = true;
                }
            }

            var absorbMod = GetAbsorbMod(target);

            bool isPVP = sourcePlayer != null && targetPlayer != null;

            //http://acpedia.org/wiki/Announcements_-_2014/01_-_Forces_of_Nature - Aegis is 72% effective in PvP
            if (isPVP && (target.CombatMode == CombatMode.Melee || target.CombatMode == CombatMode.Missile))
            {
                absorbMod  = 1 - absorbMod;
                absorbMod *= 0.72f;
                absorbMod  = 1 - absorbMod;
            }

            if (isPVP && Spell.IsHarmful)
            {
                Player.UpdatePKTimers(sourcePlayer, targetPlayer);
            }

            var elementalDamageMod = GetCasterElementalDamageModifier(sourceCreature, target, Spell.DamageType);

            // Possible 2x + damage bonus for the slayer property
            var slayerMod = GetWeaponCreatureSlayerModifier(sourceCreature, target);

            // life magic projectiles: ie., martyr's hecatomb
            if (Spell.MetaSpellType == ACE.Entity.Enum.SpellType.LifeProjectile)
            {
                lifeMagicDamage = LifeProjectileDamage * Spell.DamageRatio;

                // could life magic projectiles crit?
                // if so, did they use the same 1.5x formula as war magic, instead of 2.0x?
                if (criticalHit)
                {
                    weaponCritDamageMod = GetWeaponCritDamageMod(sourceCreature, attackSkill, target);
                    critDamageBonus     = lifeMagicDamage * 0.5f * weaponCritDamageMod;
                }

                weaponResistanceMod = GetWeaponResistanceModifier(sourceCreature, attackSkill, Spell.DamageType);

                // if attacker/weapon has IgnoreMagicResist directly, do not transfer to spell projectile
                // only pass if SpellProjectile has it directly, such as 2637 - Invoking Aun Tanua

                resistanceMod = (float)Math.Max(0.0f, target.GetResistanceMod(resistanceType, this, null, weaponResistanceMod));

                finalDamage = (lifeMagicDamage + critDamageBonus) * elementalDamageMod * slayerMod * resistanceMod * absorbMod;
            }
            // war/void magic projectiles
            else
            {
                if (criticalHit)
                {
                    // Original:
                    // http://acpedia.org/wiki/Announcements_-_2002/08_-_Atonement#Letter_to_the_Players

                    // Critical Strikes: In addition to the skill-based damage bonus, each projectile spell has a 2% chance of causing a critical hit on the target and doing increased damage.
                    // A magical critical hit is similar in some respects to melee critical hits (although the damage calculation is handled differently).
                    // While a melee critical hit automatically does twice the maximum damage of the weapon, a magical critical hit will do an additional half the minimum damage of the spell.
                    // For instance, a magical critical hit from a level 7 spell, which does 110-180 points of damage, would add an additional 55 points of damage to the spell.

                    // Later updated for PvE only:

                    // http://acpedia.org/wiki/Announcements_-_2004/07_-_Treaties_in_Stone#Letter_to_the_Players

                    // Currently when a War Magic spell scores a critical hit, it adds a multiple of the base damage of the spell to a normal damage roll.
                    // Starting in July, War Magic critical hits will instead add a multiple of the maximum damage of the spell.
                    // No more crits that do less damage than non-crits!

                    if (isPVP) // PvP: 50% of the MIN damage added to normal damage roll
                    {
                        critDamageBonus = Spell.MinDamage * 0.5f;
                    }
                    else   // PvE: 50% of the MAX damage added to normal damage roll
                    {
                        critDamageBonus = Spell.MaxDamage * 0.5f;
                    }

                    weaponCritDamageMod = GetWeaponCritDamageMod(sourceCreature, attackSkill, target);

                    critDamageBonus *= weaponCritDamageMod;
                }

                /* War Magic skill-based damage bonus
                 * http://acpedia.org/wiki/Announcements_-_2002/08_-_Atonement#Letter_to_the_Players
                 */
                if (sourcePlayer != null)
                {
                    // per retail stats, level 8 difficulty is capped to 350 instead of 400
                    // without this, level 7s have the potential to deal more damage than level 8s
                    var difficulty = Math.Min(Spell.Power, 350);    // was skillMod possibility capped to 1.3x for level 7 spells in retail, instead of level 8 difficulty cap?
                    var magicSkill = sourcePlayer.GetCreatureSkill(Spell.School).Current;

                    if (magicSkill > difficulty)
                    {
                        // Bonus clamped to a maximum of 50%
                        //var percentageBonus = Math.Clamp((magicSkill - Spell.Power) / 100.0f, 0.0f, 0.5f);
                        var percentageBonus = (magicSkill - difficulty) / 1000.0f;

                        skillBonus = Spell.MinDamage * percentageBonus;
                    }
                }
                baseDamage = ThreadSafeRandom.Next(Spell.MinDamage, Spell.MaxDamage);

                weaponResistanceMod = GetWeaponResistanceModifier(sourceCreature, attackSkill, Spell.DamageType);

                // if attacker/weapon has IgnoreMagicResist directly, do not transfer to spell projectile
                // only pass if SpellProjectile has it directly, such as 2637 - Invoking Aun Tanua

                resistanceMod = (float)Math.Max(0.0f, target.GetResistanceMod(resistanceType, this, null, weaponResistanceMod));

                if (sourcePlayer != null && targetPlayer != null && Spell.DamageType == DamageType.Nether)
                {
                    // for direct damage from void spells in pvp,
                    // apply void_pvp_modifier *on top of* the player's natural resistance to nether

                    // this supposedly brings the direct damage from void spells in pvp closer to retail
                    resistanceMod *= (float)PropertyManager.GetDouble("void_pvp_modifier").Item;
                }

                finalDamage = baseDamage + critDamageBonus + skillBonus;

                finalDamage *= elementalDamageMod * slayerMod * resistanceMod * absorbMod;
            }

            // show debug info
            if (sourceCreature != null && sourceCreature.DebugDamage.HasFlag(Creature.DebugDamageType.Attacker))
            {
                ShowInfo(sourceCreature, Spell, attackSkill, criticalChance, criticalHit, critDefended, overpower, weaponCritDamageMod, skillBonus, baseDamage, critDamageBonus, elementalDamageMod, slayerMod, weaponResistanceMod, resistanceMod, absorbMod, LifeProjectileDamage, lifeMagicDamage, finalDamage);
            }
            if (target.DebugDamage.HasFlag(Creature.DebugDamageType.Defender))
            {
                ShowInfo(target, Spell, attackSkill, criticalChance, criticalHit, critDefended, overpower, weaponCritDamageMod, skillBonus, baseDamage, critDamageBonus, elementalDamageMod, slayerMod, weaponResistanceMod, resistanceMod, absorbMod, LifeProjectileDamage, lifeMagicDamage, finalDamage);
            }
            return(finalDamage);
        }
예제 #21
0
 /// <summary>
 /// Returns a divisor for the target height
 /// for aiming projectiles
 /// </summary>
 public virtual float GetAimHeight(WorldObject target)
 {
     return(2.0f);
 }
예제 #22
0
        /// <summary>
        /// Called for a spell projectile to damage its target
        /// </summary>
        public void DamageTarget(Creature target, float damage, bool critical, bool critDefended, bool overpower)
        {
            var targetPlayer = target as Player;

            if (targetPlayer != null && targetPlayer.Invincible || target.IsDead)
            {
                return;
            }

            var sourceCreature = ProjectileSource as Creature;
            var sourcePlayer   = ProjectileSource as Player;

            var amount                = 0u;
            var percent               = 0.0f;
            var heritageMod           = 1.0f;
            var sneakAttackMod        = 1.0f;
            var damageRatingMod       = 1.0f;
            var damageResistRatingMod = 1.0f;

            WorldObject equippedCloak = null;

            // handle life projectiles for stamina / mana
            if (Spell.Category == SpellCategory.StaminaLowering)
            {
                percent = damage / target.Stamina.MaxValue;
                amount  = (uint)-target.UpdateVitalDelta(target.Stamina, (int)-Math.Round(damage));
            }
            else if (Spell.Category == SpellCategory.ManaLowering)
            {
                percent = damage / target.Mana.MaxValue;
                amount  = (uint)-target.UpdateVitalDelta(target.Mana, (int)-Math.Round(damage));
            }
            else
            {
                // for possibly applying sneak attack to magic projectiles,
                // only do this for health-damaging projectiles?
                if (sourcePlayer != null)
                {
                    // TODO: use target direction vs. projectile position, instead of player position
                    // could sneak attack be applied to void DoTs?
                    sneakAttackMod = sourcePlayer.GetSneakAttackMod(target);
                    //Console.WriteLine("Magic sneak attack:  + sneakAttackMod);
                    heritageMod = sourcePlayer.GetHeritageBonus(sourcePlayer.GetEquippedWand()) ? 1.05f : 1.0f;
                }

                // DR / DRR applies for magic too?
                var damageRating = sourceCreature?.GetDamageRating() ?? 0;
                damageRatingMod       = Creature.AdditiveCombine(Creature.GetPositiveRatingMod(damageRating), heritageMod, sneakAttackMod);
                damageResistRatingMod = target.GetDamageResistRatingMod(CombatType.Magic);
                damage *= damageRatingMod * damageResistRatingMod;

                percent = damage / target.Health.MaxValue;

                //Console.WriteLine($"Damage rating: " + Creature.ModToRating(damageRatingMod));

                equippedCloak = target.EquippedCloak;

                if (equippedCloak != null && Cloak.HasDamageProc(equippedCloak) && Cloak.RollProc(equippedCloak, percent))
                {
                    var reducedDamage = Cloak.GetReducedAmount(ProjectileSource, damage);

                    Cloak.ShowMessage(target, ProjectileSource, damage, reducedDamage);

                    damage  = reducedDamage;
                    percent = damage / target.Health.MaxValue;
                }

                amount = (uint)-target.UpdateVitalDelta(target.Health, (int)-Math.Round(damage));
                target.DamageHistory.Add(ProjectileSource, Spell.DamageType, amount);

                //if (targetPlayer != null && targetPlayer.Fellowship != null)
                //targetPlayer.Fellowship.OnVitalUpdate(targetPlayer);
            }

            amount = (uint)Math.Round(damage);    // full amount for debugging

            if (target.IsAlive)
            {
                string verb = null, plural = null;
                Strings.GetAttackVerb(Spell.DamageType, percent, ref verb, ref plural);
                var type = Spell.DamageType.GetName().ToLower();

                var critMsg      = critical ? "Critical hit! " : "";
                var sneakMsg     = sneakAttackMod > 1.0f ? "Sneak Attack! " : "";
                var overpowerMsg = overpower ? "Overpower! " : "";

                var nonHealth = Spell.Category == SpellCategory.StaminaLowering || Spell.Category == SpellCategory.ManaLowering;

                if (sourcePlayer != null)
                {
                    var critProt = critDefended ? " Your critical hit was avoided with their augmentation!" : "";

                    var attackerMsg = $"{critMsg}{overpowerMsg}{sneakMsg}You {verb} {target.Name} for {amount} points with {Spell.Name}.{critProt}";

                    // could these crit / sneak attack?
                    if (nonHealth)
                    {
                        var vital = Spell.Category == SpellCategory.StaminaLowering ? "stamina" : "mana";
                        attackerMsg = $"With {Spell.Name} you drain {amount} points of {vital} from {target.Name}.";
                    }

                    if (!sourcePlayer.SquelchManager.Squelches.Contains(target, ChatMessageType.Magic))
                    {
                        sourcePlayer.Session.Network.EnqueueSend(new GameMessageSystemChat(attackerMsg, ChatMessageType.Magic));
                    }
                }

                if (targetPlayer != null)
                {
                    var critProt = critDefended ? " Your augmentation allows you to avoid a critical hit!" : "";

                    var defenderMsg = $"{critMsg}{overpowerMsg}{sneakMsg}{ProjectileSource.Name} {plural} you for {amount} points with {Spell.Name}.{critProt}";

                    if (nonHealth)
                    {
                        var vital = Spell.Category == SpellCategory.StaminaLowering ? "stamina" : "mana";
                        defenderMsg = $"{ProjectileSource.Name} casts {Spell.Name} and drains {amount} points of your {vital}.";
                    }

                    if (!targetPlayer.SquelchManager.Squelches.Contains(ProjectileSource, ChatMessageType.Magic))
                    {
                        targetPlayer.Session.Network.EnqueueSend(new GameMessageSystemChat(defenderMsg, ChatMessageType.Magic));
                    }

                    if (sourceCreature != null)
                    {
                        targetPlayer.SetCurrentAttacker(sourceCreature);
                    }
                }

                if (!nonHealth)
                {
                    if (equippedCloak != null && Cloak.HasProcSpell(equippedCloak))
                    {
                        Cloak.TryProcSpell(target, ProjectileSource, equippedCloak, percent);
                    }

                    target.EmoteManager.OnDamage(sourcePlayer);

                    if (critical)
                    {
                        target.EmoteManager.OnReceiveCritical(sourcePlayer);
                    }
                }
            }
            else
            {
                var lastDamager = ProjectileSource != null ? new DamageHistoryInfo(ProjectileSource) : null;
                target.OnDeath(lastDamager, Spell.DamageType, critical);
                target.Die();
            }

            // show debug info
            if (sourceCreature != null && sourceCreature.DebugDamage.HasFlag(Creature.DebugDamageType.Attacker))
            {
                ShowInfo(sourceCreature, heritageMod, sneakAttackMod, damageRatingMod, damageResistRatingMod, damage);
            }
            if (target.DebugDamage.HasFlag(Creature.DebugDamageType.Defender))
            {
                ShowInfo(target, heritageMod, sneakAttackMod, damageRatingMod, damageResistRatingMod, damage);
            }
        }
예제 #23
0
        public float GetSneakAttackMod(WorldObject target)
        {
            // ensure trained
            var sneakAttack = GetCreatureSkill(Skill.SneakAttack);

            if (sneakAttack.AdvancementClass < SkillAdvancementClass.Trained)
            {
                return(1.0f);
            }

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

            if (creatureTarget == null)
            {
                return(1.0f);
            }

            // Effects:
            // General Sneak Attack effects:
            //   - 100% chance to sneak attack from behind an opponent.
            //   - Deception trained: 10% chance to sneak attack from the front of an opponent
            //   - Deception specialized: 15% chance to sneak attack from the front of an opponent
            var angle  = creatureTarget.GetAngle(this);
            var behind = Math.Abs(angle) > 90.0f;
            var chance = 0.0f;

            if (behind)
            {
                chance = 1.0f;
            }
            else
            {
                var deception = GetCreatureSkill(Skill.Deception);
                if (deception.AdvancementClass == SkillAdvancementClass.Trained)
                {
                    chance = 0.1f;
                }
                else if (deception.AdvancementClass == SkillAdvancementClass.Specialized)
                {
                    chance = 0.15f;
                }

                // if Deception is below 306 skill, these chances are reduced proportionately.
                // this is in addition to proprtional reduction if your Sneak Attack skill is below your attack skill.
                var deceptionCap = 306;
                if (deception.Current < deceptionCap)
                {
                    chance *= Math.Min((float)deception.Current / deceptionCap, 1.0f);
                }
            }
            //Console.WriteLine($"Sneak attack {(behind ? "behind" : "front")}, chance {Math.Round(chance * 100)}%");

            var rng = ThreadSafeRandom.Next(0.0f, 1.0f);

            if (rng > chance)
            {
                return(1.0f);
            }

            // Damage Rating:
            // Sneak Attack Trained:
            //   + 10 Damage Rating when Sneak Attack activates
            // Sneak Attack Specialized:
            //   + 20 Damage Rating when Sneak Attack activates
            var damageRating = sneakAttack.AdvancementClass == SkillAdvancementClass.Specialized ? 20.0f : 10.0f;

            // Sneak Attack works for melee, missile, and magic attacks.

            // if the Sneak Attack skill is lower than your attack skill (as determined by your equipped weapon)
            // then the damage rating is reduced proportionately. Because the damage rating caps at 10 for trained
            // and 20 for specialized, there is no reason to raise the skill above your attack skill
            var attackSkill = GetCreatureSkill(GetCurrentAttackSkill());

            if (sneakAttack.Current < attackSkill.Current)
            {
                if (attackSkill.Current > 0)
                {
                    damageRating *= (float)sneakAttack.Current / attackSkill.Current;
                }
                else
                {
                    damageRating = 0;
                }
            }

            // if the defender has Assess Person, they reduce the extra Sneak Attack damage Deception can add
            // from the front by up to 100%.
            // this percent is reduced proportionately if your buffed Assess Person skill is below the deception cap.
            // this reduction does not apply to attacks from behind.
            if (!behind)
            {
                // compare to assess person or deception??
                // wiki info is confusing here, it says 'your buffed Assess Person'
                // which sounds like its scaling sourceAssess / targetAssess,
                // but i think it should be targetAssess / deceptionCap?
                var targetAssess = creatureTarget.GetCreatureSkill(Skill.AssessPerson).Current;

                var deceptionCap = 306;
                damageRating *= 1.0f - Math.Min((float)targetAssess / deceptionCap, 1.0f);
            }

            var sneakAttackMod = (100 + damageRating) / 100.0f;

            //Console.WriteLine("SneakAttackMod: " + sneakAttackMod);
            return(sneakAttackMod);
        }
예제 #24
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.Guid.Full, 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(player.Guid.Full, 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.Broadcast));

                            var lockpickSkill = player.GetCreatureSkill(Skill.Lockpick);
                            Proficiency.OnSuccessUse(player, lockpickSkill, difficulty);
                        }
                        else
                        {
                            player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{target.Name} has been unlocked.", ChatMessageType.Broadcast));
                        }

                        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();
        }
예제 #25
0
 public override void SetLinkProperties(WorldObject wo)
 {
     wo.ActivationTarget = Guid.Full;
 }
예제 #26
0
 /// <summary>
 /// Compares the number of body parts covered by 2 pieces of clothing
 /// </summary>
 public int ValidLocationComparer(WorldObject a, WorldObject b)
 {
     return(EnumHelper.NumFlags((uint)a.ValidLocations).CompareTo(EnumHelper.NumFlags((uint)b.ValidLocations)));
 }
예제 #27
0
 public override void ActOnUse(WorldObject wo)
 {
     // Do nothing
 }
예제 #28
0
 /// <summary>
 /// Compares the number of body parts covered by 2 pieces of clothing
 /// </summary>
 public int ArmorLevelComparer(WorldObject a, WorldObject b)
 {
     return(((uint)a.ArmorLevel).CompareTo((uint)b.ArmorLevel));
 }
예제 #29
0
 public WorldObject(ACE.Server.WorldObjects.WorldObject wo)
 {
     _wo = wo;
 }
예제 #30
0
        public void HandleActionUseOnTarget(Player player, WorldObject target)
        {
            ActionChain chain = new ActionChain();


            chain.AddAction(player, () =>
            {
                if (target.WeenieType == WeenieType.Door)
                {
                    var door = (Door)target;

                    if (player.Skills[Skill.Lockpick].Status != SkillStatus.Trained && player.Skills[Skill.Lockpick].Status != SkillStatus.Specialized)
                    {
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouArentTrainedInLockpicking));
                        return;
                    }

                    void consume(Player plr, Lockpick lp)
                    {
                        lp.Structure--;
                        if (lp.Structure < 1)
                        {
                            plr.TryRemoveItemFromInventoryWithNetworking(this, 1);
                        }
                        plr.Session.Network.EnqueueSend(new GameEventUseDone(plr.Session));
                        plr.Session.Network.EnqueueSend(new GameMessagePublicUpdatePropertyInt(lp, PropertyInt.Structure, (int)lp.Structure));
                        plr.Session.Network.EnqueueSend(new GameMessageSystemChat($"Uses reamaining: {Structure}", ChatMessageType.System));
                    }

                    Door.UnlockDoorResults results = door.UnlockDoor(player.Skills[Skill.Lockpick].Current);
                    switch (results)
                    {
                    case Door.UnlockDoorResults.UnlockSuccess:
                        consume(player, this);
                        break;

                    case Door.UnlockDoorResults.DoorOpen:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockWhatIsOpen));
                        break;

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

                    case Door.UnlockDoorResults.PickLockFailed:
                        consume(player, this);
                        break;

                    default:
                        player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.KeyDoesntFitThisLock));
                        break;
                    }
                }
                else if (target.WeenieType == WeenieType.Chest)
                {
                    var message = new GameMessageSystemChat($"Unlocking {target.Name} has not been implemented, yet!", ChatMessageType.System);
                    player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session), message);
                }
                else
                {
                    player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat));
                }
            });

            chain.EnqueueChain();
        }