Ejemplo n.º 1
0
        /// <summary>
        /// Returns price and time to level up ability for character.
        /// </summary>
        /// <param name="character"></param>
        /// <param name="abilityData"></param>
        /// <param name="abilityTreeData"></param>
        /// <param name="level"></param>
        /// <param name="price"></param>
        /// <param name="time"></param>
        public static void Get(Character character, AbilityData abilityData, AbilityTreeData abilityTreeData, int abilityLevel, out int price, out int time)
        {
            price = 999999;
            time  = 999999;

            // This is all a little temporary. I don't know if we might want
            // to use Lua for this, or maybe something else entirely.
            // If we leave it as C#, we might not want to use reflection,
            // or at least cache the methods, but for now it will work fine.

            var call  = abilityTreeData.PriceTime;
            var match = CallRegex.Match(call);

            if (!match.Success)
            {
                Log.Warning("AbilityPriceTime.Get: Invalid unlock call '{0}'.", call);
                return;
            }

            var funcName         = match.Groups["funcName"].Value;
            var abilityClassName = abilityData.ClassName;
            var maxLevel         = abilityTreeData.MaxLevel;

            var method = typeof(AbilityPriceTime).GetMethod(funcName);

            if (method == null)
            {
                Log.Warning("AbilityPriceTime.Get: Unknown function '{0}'.", funcName);
                return;
            }

            var parameters = method.GetParameters();

            if (
                method.ReturnType != typeof(void) ||
                parameters.Length != 6 ||
                parameters[0].ParameterType != typeof(Character) ||
                parameters[1].ParameterType != typeof(string) ||
                parameters[2].ParameterType != typeof(int) ||
                parameters[3].ParameterType != typeof(int) ||
                parameters[4].ParameterType.GetElementType() != typeof(int) || !parameters[4].IsOut ||
                parameters[5].ParameterType.GetElementType() != typeof(int) || !parameters[5].IsOut
                )
            {
                Log.Warning("AbilityPriceTime.Get: Function '{0}' has an invalid signature.", funcName);
                return;
            }

            var funcParameters = new object[] { character, abilityClassName, abilityLevel, maxLevel, null, null };

            method.Invoke(null, funcParameters);

            price = (int)funcParameters[4];
            time  = (int)funcParameters[5];
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Returns true if given ability is unlocked for character.
        /// </summary>
        /// <param name="character"></param>
        /// <param name="abilityTreeData"></param>
        /// <returns></returns>
        public static bool IsUnlocked(Character character, AbilityData abilityData, AbilityTreeData abilityTreeData)
        {
            // This is all a little temporary. I don't know if we might want
            // to use Lua for this, or maybe something else entirely.
            // If we leave it as C#, we might not want to use reflection,
            // or at least cache the methods, but for now it will work fine.

            var call  = abilityTreeData.Unlock;
            var match = UnlockCallRegex.Match(call);

            if (!match.Success)
            {
                Log.Warning("AbilityUnlock.IsUnlocked: Invalid unlock call '{0}'.", call);
                return(false);
            }

            var funcName = match.Groups["funcName"].Value;
            var strParam = match.Groups["strParam"].Value;
            var numParam = Convert.ToInt32(match.Groups["numParam"].Value);

            var method = typeof(AbilityUnlock).GetMethod(funcName);

            if (method == null)
            {
                Log.Warning("AbilityUnlock.IsUnlocked: Unknown function '{0}'.", funcName);
                return(false);
            }

            var parameters = method.GetParameters();

            if (
                method.ReturnType != typeof(bool) ||
                parameters.Length != 4 ||
                parameters[0].ParameterType != typeof(Character) ||
                parameters[1].ParameterType != typeof(string) ||
                parameters[2].ParameterType != typeof(int) ||
                parameters[3].ParameterType != typeof(AbilityData)
                )
            {
                Log.Warning("AbilityUnlock.IsUnlocked: Function '{0}' has an invalid signature.", funcName);
                return(false);
            }

            var result = (bool)method.Invoke(null, new object[] { character, strParam, numParam, abilityData });

            return(result);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Official slash command to learn abilities.
        /// </summary>
        /// <param name="conn"></param>
        /// <param name="sender"></param>
        /// <param name="target"></param>
        /// <param name="command"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private CommandResult HandleLearnPcAbil(ChannelConnection conn, Character sender, Character target, string command, string[] args)
        {
            // Since this command is sent via UI interactions, we'll not
            // use any automated command result messages, but we'll leave
            // debug messages for now, in case of unexpected values.

            if (args.Length != 3 || !int.TryParse(args[2], out var levels) || levels < 1)
            {
                Log.Debug("HandleLearnPcAbil: Invalid call by user '{0}': {1}", conn.Account.Name, command);
                return(CommandResult.Okay);
            }

            var className = args[1];

            var abilityData = ChannelServer.Instance.Data.AbilityDb.Find(className);

            if (abilityData == null)
            {
                Log.Debug("HandleLearnPcAbil: User '{0}' tried to learn non-existent ability '{1}'.", conn.Account.Name, className);
                return(CommandResult.Okay);
            }

            // All we get here is the ability name, but whether it can
            // be learned or not potentially depends on any of the job's
            // ability tree's entries. We have to check whether the ability
            // can be learned by any of the character's jobs.

            var abilityId = abilityData.Id;
            var canLearn  = false;
            var jobs      = sender.Jobs.GetList();

            AbilityTreeData abilityTreeData = null;

            foreach (var job in jobs)
            {
                // An ability can be learned by a job if there's an entry
                // for it in the tree and an unlock condition is given.
                var jobAbilityTreeData = ChannelServer.Instance.Data.AbilityTreeDb.Find(job.Id, abilityId);
                if (jobAbilityTreeData != null && jobAbilityTreeData.HasUnlock)
                {
                    var unlocked = AbilityUnlock.IsUnlocked(sender, abilityData, jobAbilityTreeData);
                    if (unlocked)
                    {
                        canLearn        = true;
                        abilityTreeData = jobAbilityTreeData;
                        break;
                    }
                }
            }

            if (!canLearn)
            {
                Log.Debug("HandleLearnPcAbil: User '{0}' tried to learn ability '{1}', which they can't learn (yet).", conn.Account.Name, className);
                return(CommandResult.Okay);
            }

            var ability      = sender.Abilities.Get(abilityId);
            var currentLevel = (ability == null ? 0 : ability.Level);
            var newLevel     = (currentLevel + levels);
            var maxLevel     = abilityTreeData.MaxLevel;

            if (newLevel > maxLevel)
            {
                Log.Debug("HandleLearnPcAbil: User '{0}' tried to increase ability '{1}'s level past the max level of {2}.", conn.Account.Name, className, maxLevel);
                return(CommandResult.Okay);
            }

            // Price and time can come either from the actual values,
            // or from functions that return both.

            var price = abilityTreeData.Price;
            var time  = abilityTreeData.Time;

            if (abilityTreeData.HasPriceTime)
            {
                price = 0;

                for (var i = currentLevel + 1; i <= newLevel; ++i)
                {
                    AbilityPriceTime.Get(sender, abilityData, abilityTreeData, i, out var addPrice, out time);
                    price += addPrice;
                }
            }

            var points = sender.AbilityPoints;

            if (points < price)
            {
                Log.Debug("HandleLearnPcAbil: User '{0}' didn't have enough points.", conn.Account.Name);
                return(CommandResult.Okay);
            }

            //Log.Debug("Learn: {0}", abilityData.EngName);
            //Log.Debug("- From: {0}", currentLevel);
            //Log.Debug("- To: {0}", newLevel);
            //Log.Debug("- Price: {0}", price);
            //Log.Debug("- Time: {0}", time);

            // Add ability if character doesn't have it yet
            if (ability == null)
            {
                ability = new Ability(abilityId, 0);
                sender.Abilities.Add(ability);
            }

            // Update ability
            ability.Level += levels;
            Send.ZC_OBJECT_PROPERTY(sender.Connection, ability);

            sender.ModifyAbilityPoints(-price);
            Send.ZC_ADDON_MSG(sender, AddonMessage.RESET_ABILITY_UP, "Ability_" + abilityTreeData.Category);
            Send.ZC_ADDON_MSG(sender, AddonMessage.SUCCESS_LEARN_ABILITY, abilityTreeData.Category);

            return(CommandResult.Okay);
        }