Ejemplo n.º 1
0
        /// <summary>
        /// Handles skill casting. Will return false if skill is unable to cast for any reason, such as not enough resources or invalid skill.
        /// </summary>
        /// <param name="nSkillID"></param>
        /// <param name="bLeft">If the skill is happening to the left of the character</param>
        /// <param name="bOutsideHandling">When this is true, the function will only process the resource/cooldown portion.</param>
        /// <returns></returns>
        public bool Cast(int nSkillID, bool bLeft, bool bOutsideHandling = false, int nSpiritJavelinItemID = 0)
        {
            if (Parent.Stats.nHP <= 0)
            {
                return(false);                                   // REEEEEEEEE
            }
#if DEBUG
            Parent.SendMessage("Casting spell " + nSkillID);
#endif

            var skill = Get(nSkillID, true);

            if (skill == null)
            {
                Parent.SendMessage("Unable to find skill.");
                return(false);
            }

            var template = skill.Template;
            var nSLV     = skill.nSLV;

            if (Parent.Cooldowns.OnCooldown(nSkillID))
            {
                Parent.SendMessage("Trying to cast skill while skill on cooldown.");
                return(false);
            }

            double costMp        = skill.MPCost;                    // done
            var    costMeso      = template.MesoR(nSLV);            // may not be done..
            var    itemConsumeId = template.ItemConsume;            // done
            if (Parent.Buffs.Contains((int)Skills.BISHOP_INFINITY)) // party buff
            {
                costMp = 0;
            }
            else
            {
                switch (Parent.Stats.nJob / 10)
                {
                case 21:
                    if (Parent.Buffs.Contains((int)Skills.ARCHMAGE1_INFINITY))
                    {
                        costMp = 0;
                        break;
                    }
                    costMp *= (Parent.Skills.Get((int)Skills.MAGE1_ELEMENT_AMPLIFICATION)?.X_Effect ?? 100.0) * 0.01;
                    goto default;

                case 22:
                    if (Parent.Buffs.Contains((int)Skills.ARCHMAGE2_INFINITY))
                    {
                        costMp = 0;
                        break;
                    }
                    costMp *= (Parent.Skills.Get((int)Skills.MAGE2_ELEMENT_AMPLIFICATION)?.X_Effect ?? 100.0) * 0.01;
                    goto default;

                case 121:                         // koc mage
                    costMp *= (Parent.Skills.Get((int)Skills.FLAMEWIZARD_ELEMENT_AMPLIFICATION)?.X_Effect ?? 100.0) * 0.01;
                    goto default;

                case 221:                         // evan
                case 222:                         // evan rounded up (2215-2218)
                    costMp *= (Parent.Skills.Get((int)Skills.EVAN_ELEMENT_AMPLIFICATION)?.X_Effect ?? 100.0) * 0.01;
                    goto default;

                case 31:
                    if (Parent.Buffs[(int)Skills.BOWMASTER_CONCENTRATION] is BuffSkill pBuff)
                    {
                        costMp *= 1 - (pBuff.Template.X(pBuff.nSLV) * 0.01);
                    }
                    goto default;

                default:
                    if (Parent.Stats.nMP < Math.Floor(costMp))
                    {
                        Log.Debug($"Not enough mp to perform skill. Current MP: {Parent.Stats.nMP}. MP required: {costMp}. SkillID: {nSkillID}. CharID: {Parent.dwId}. CharName: {Parent.Stats.sCharacterName}.");
                        return(false);
                    }
                    break;
                }
            }
            // calculate HP cost
            int costHp;
            switch ((Skills)nSkillID)
            {
            case Skills.DUAL5_FINAL_CUT:
            case Skills.DRAGONKNIGHT_DRAGON_ROAR:
            case Skills.DRAGONKNIGHT_SACRIFICE:
            case Skills.INFIGHTER_MP_RECOVERY:
                costHp = (int)(Parent.BasicStats.nMHP * template.X(skill.nSLV) * 0.01);
                break;

            default:
                costHp = 0;
                break;
            }

            if (Parent.Stats.nHP <= costHp)
            {
                Log.Debug($"Not enough hp to perform skill. Hp required: {costHp}");
                return(false);
            }

            if (Parent.Stats.nMoney < costMeso)
            {
                Log.Debug($"Not enough meso to perform skill. Meso required: {costMeso}");
                return(false);
            }

            var itemConsumeAmount = template.ItemConsumeAmount;

            if (nSkillID == (int)Skills.NIGHTLORD_SPIRIT_JAVELIN)
            {
                if (!ItemConstants.IsThrowingStar(nSpiritJavelinItemID))
                {
                    return(false);
                }

                itemConsumeId     = nSpiritJavelinItemID;
                itemConsumeAmount = 200;                 // we hardcodin' bois
            }

            if (itemConsumeId > 0 && itemConsumeAmount > 0)
            {
                var valid    = false;
                var itemType = ItemConstants.GetInventoryType(itemConsumeId);
                foreach (var item in itemType == InventoryType.Etc ? Parent.InventoryEtc : Parent.InventoryConsume)                 // lol ternary operator in a loop
                {
                    if (item.Value.nItemID == itemConsumeId && item.Value.nNumber >= itemConsumeAmount)
                    {
                        valid = true;
                        InventoryManipulator.RemoveFrom(Parent, itemType, item.Key, (short)itemConsumeAmount);
                        break;
                    }
                }

                if (!valid)
                {
                    return(false);
                }
            }

            // energy skills
            switch ((Skills)nSkillID)
            {
            case Skills.BUCCANEER_ENERGY_BURSTER:
            case Skills.STRIKER_ENERGY_BURSTER:
            case Skills.BUCCANEER_ENERGY_DRAIN:
            case Skills.STRIKER_ENERGY_DRAIN:
            case Skills.VIPER_ENERGY_ORB:
                if (Parent.Combat.nEnergy != SkillLogic.EnergyMax)
                {
                    Parent.SendMessage($"Insufficient energy. ({Parent.Combat.nEnergy} != {SkillLogic.EnergyMax}).");
                    return(false);
                }
                break;
            }

            Parent.Modify.Stats(ctx =>
            {
                if (costHp > 0)
                {
                    ctx.HP -= costHp;
                }

                if (costMp > 0)
                {
                    ctx.MP -= (int)Math.Floor(costMp);
                }

                if (costMeso > 0)
                {
                    ctx.Money -= costMeso;
                }
            });

            if (Parent.nPreparedSkill == 0)
            {
                var nCdSkillId = nSkillID;
                var nCdTime    = template.Cooltime(skill.nSLV);

                switch ((Skills)nSkillID)
                {
                case Skills.MECHANIC_SAFETY:
                {
                    if (!Parent.Field.Summons.Any(s => s.dwParentID == dwParentID &&
                                                  (s.nSkillID == (int)Skills.MECHANIC_SATELITE || s.nSkillID == (int)Skills.MECHANIC_SATELITE2 || s.nSkillID == (int)Skills.MECHANIC_SATELITE3)))
                    {
                        return(false);                                        // satellites must be active to cast this skill
                    }
                }
                break;

                case Skills.WILDHUNTER_SWALLOW:
                case Skills.MECHANIC_TESLA_COIL:
                    nCdTime = 0;
                    break;

                case Skills.WILDHUNTER_SWALLOW_DUMMY_ATTACK:
                case Skills.WILDHUNTER_SWALLOW_DUMMY_BUFF:
                    var pTempTemplate = Parent.Skills.Get((int)Skills.WILDHUNTER_SWALLOW);
                    nCdSkillId = pTempTemplate.nSkillID;
                    nCdTime    = (short)pTempTemplate.CoolTimeSeconds;
                    break;
                }

                if (nCdTime > 0)
                {
                    Parent.Cooldowns.UpdateOrInsert(nCdSkillId, (short)nCdTime);
                }
            }

            if (template.is_heros_will_skill)
            {
                Parent.Buffs.CancelAllDebuffs();
                return(true);
            }

            if (bOutsideHandling || template.IsNotBuff)
            {
                return(true);
            }

            if (SkillLogic.is_teleport_mastery_skill(nSkillID))
            {
                HandleTeleportMastery(nSkillID, nSLV);
                return(true);
            }

            switch ((Skills)nSkillID)
            {
            case Skills.HERO_ENRAGE:
            {
                Parent.Buffs.Remove((int)Skills.CRUSADER_COMBO_ATTACK);
                Parent.Combat.ComboCounter = 0;
            }
            break;

            case Skills.CRUSADER_MAGIC_CRASH:
            case Skills.DRAGONKNIGHT_MAGIC_CRASH:
            case Skills.KNIGHT_MAGIC_CRASH:
                CastAOEMobStat(nSkillID);
                return(true);

            case Skills.NIGHTLORD_SPIRIT_JAVELIN:
            {
                Parent.Buffs.Remove(nSkillID);
                var buff = new BuffSkill(nSkillID, nSLV);
                buff.GenerateSpiritJavelin(nSpiritJavelinItemID);
                Parent.Buffs.Add(buff);
            }
                return(true);

            case Skills.BMAGE_SUPER_BODY:
                DoSuperBody(nSLV);
                return(true);

            case Skills.BMAGE_AURA_BLUE:
                DoAuraSkill(SecondaryStatFlag.BlueAura, (int)Skills.BMAGE_AURA_BLUE_ADVANCED, nSkillID, nSLV);
                return(true);

            case Skills.BMAGE_AURA_YELLOW:
                DoAuraSkill(SecondaryStatFlag.YellowAura, (int)Skills.BMAGE_AURA_YELLOW_ADVANCED, nSkillID, nSLV);
                return(true);

            case Skills.BMAGE_AURA_DARK:
                DoAuraSkill(SecondaryStatFlag.DarkAura, (int)Skills.BMAGE_AURA_DARK_ADVANCED, nSkillID, nSLV);
                return(true);

            case Skills.SHADOWER_SMOKE_SHELL:
            case Skills.EVAN_RECOVERY_AURA:
            case Skills.MAGE1_POISON_MIST:
            case Skills.FLAMEWIZARD_FLAME_GEAR:
            case Skills.BMAGE_SHELTER:
                CastAffectedAreaSkill
                    (nSkillID, skill.nSLV, (short)skill.BuffTime, Parent.Position.CurrentXY, template.LT, template.RB);
                return(true);

            case Skills.THIEFMASTER_CHAKRA:
                if (Parent.Stats.nHP < Parent.BasicStats.nMHP * 0.5)
                {
                    // BMS
                    // dwFlaga = sd->nY + 100;
                    // tCur = CRand32::Random(&g_rand) % 100 + 100;
                    // CQWUser::IncHP(v5, ((tCur * v5->m_basicStat.nLUK * 0.033 + v5->m_basicStat.nDEX) * dwFlaga * 0.002), 0);
                    var nMultiplier = skill.Y_Effect + 100;
                    var nRand       = Constants.Rand.Next() % 100 + 100;
                    Parent.Modify.Heal((int)((nRand * Parent.BasicStats.nLUK * 0.033 + Parent.BasicStats.nDEX) * nMultiplier * 0.002));
                    return(true);
                }
                return(false);

            case Skills.KNIGHT_RESTORATION:
                Parent.Modify.Heal((int)(Parent.BasicStats.nMHP * skill.X_Effect));
                return(true);

            case Skills.FLAMEWIZARD_SLOW:
            case Skills.WIZARD1_SLOW:
            case Skills.WIZARD2_SLOW:
            case Skills.NIGHTLORD_NINJA_AMBUSH:
            case Skills.SHADOWER_NINJA_AMBUSH:
            case Skills.HERMIT_SHADOW_WEB:
            case Skills.NIGHTWALKER_SHADOW_WEB:
            case Skills.DUAL3_FLASH_BANG:
            case Skills.DUAL4_UPPER_STAB:
            case Skills.HUNTER_ARROW_BOMB:
            case Skills.BOWMASTER_VENGEANCE:
            case Skills.PAGE_THREATEN:
                CastAOEMobStat(nSkillID);
                return(true);

            case Skills.DUAL4_OWL_DEATH:
                if (skill.DoProp())
                {
                    Parent.Buffs.AddSkillBuff(nSkillID, nSLV, Parent.Buffs.BuffTimeModifier());
                    Parent.Combat.OwlSpiritCount = 10;
                }
                return(true);

            case Skills.ADMIN_HOLY_SYMBOL:
                foreach (var pChar in Parent.Field.Users)
                {
                    pChar.Buffs.AddSkillBuff(nSkillID, nSLV, Parent.Buffs.BuffTimeModifier());
                }
                return(true);

            case Skills.WILDHUNTER_SWALLOW:
                SwallowMob(Parent.m_dwSwallowMobID, nSLV);
                return(true);

            case Skills.VALKYRIE_DICE:
            case Skills.MECHANIC_DICE:
            case Skills.BUCCANEER_DICE:
                DoDice(nSkillID, nSLV);
                return(true);

            case Skills.INFIGHTER_MP_RECOVERY:
                Parent.Modify.Heal(0, (int)(Parent.BasicStats.nMHP * (0.5 + (0.1 * nSLV))));                         // hard coding ftw
                return(true);

            case Skills.CLERIC_HEAL:
                DoPartyHeal(nSkillID, template.HP(nSLV));
                return(true);

            case Skills.BISHOP_RESURRECTION:
                if (Parent.Party?.Count > 1)
                {
                    Parent.Party.ApplyBuffToParty(Parent, Parent.Field.dwUniqueId, nSkillID, nSLV);
                }
                return(true);

            case Skills.PRIEST_DISPEL:
                if (Parent.Party?.Count > 1)
                {
                    Parent.Party.ApplyBuffToParty(Parent, Parent.Field.dwUniqueId, nSkillID, nSLV);
                }
                else
                {
                    Parent.Buffs.CancelAllDebuffs();
                }
                CastAOEMobStat(nSkillID);                         // lazy impl
                return(true);

            case Skills.KNIGHT_COMBAT_ORDERS:
                DoCombatOrders(nSkillID, nSLV);
                return(true);

            case Skills.BISHOP_INFINITY:                     // only a party buff for clerics (custom)
                if (Parent.Party?.Count > 1)
                {
                    Parent.Party.ApplyBuffToParty(Parent, Parent.Field.dwUniqueId, nSkillID, nSLV, 2);
                    return(true);
                }
                break;

            case Skills.VIPER_TIME_LEAP:
                DoTimeleap(nSkillID);
                return(true);

            case Skills.MECHANIC_HN07:
            {
                if (Parent.Skills.Get((int)Skills.MECHANIC_HN07_UPGRADE, false) is SkillEntry se)
                {
                    Parent.Buffs.AddSkillBuff(se.nSkillID, se.nSLV);
                    return(true);
                }
            }
            break;
            }

            if (template.Time(nSLV) != 0)
            {
                var buffTimeModifier = Parent.Buffs.BuffTimeModifier();

                if (Parent.Party?.Count > 1 && template.IsPartyBuff)
                {
                    Parent.Party.ApplyBuffToParty
                        (Parent, Parent.Field.dwUniqueId, nSkillID, nSLV, buffTimeModifier);
                }
                else
                {
                    Parent.Buffs.AddSkillBuff(nSkillID, nSLV, buffTimeModifier);
                }
            }

            return(true);
        }